Overview

Scimax supports Marp slide decks on its own: a Markdown file whose front matter has marp: true. It renders the slides with its own copy of Marp Core, so the Marp for VS Code extension is not needed.

TaskHow
See one slide as you editSlide preview: the play-preview button in the editor title bar (Slide Preview)
See and rearrange the whole deckSlide Sorter (Slide Thumbnails, Editing Slides)
PresentC-c C-e x, the play button, or a marp: link in an org file (Slideshow)
Write directivesCompletion and hover in the front matter and comments (Directives)
Write the talk in orgC-c C-e m s in the org file (Writing Slides in Org)
ExportC-c C-e for PDF, PowerPoint, Google Slides, HTML, images (Exporting Slides)

For help, Scimax: Marp Help (cmd:scimax.marp.help, also at the bottom of the Marp menu, and as C-c C-e ?) opens this guide or the online Marp documentation: slide syntax, directives, image syntax, fragmented lists, theme CSS, the built-in themes and Marp CLI.

To make a Markdown file a deck, run Scimax: Make Marp Deck (cmd:scimax.marp.insertHeader), also in the editor's right-click menu of a Markdown file that is not a deck yet. It inserts the header below, with the theme and page numbers as choices (Tab moves between them), and presenter: true so the deck gets the presenter tools (see Presenter Tools and Live Python); delete that line for a plain slideshow. If the file already has front matter, it adds marp: true to it, and presenter: true unless the front matter already sets presenter.

A minimal deck:

---
marp: true
theme: default
paginate: true
presenter: true
---

# Title slide

---

## Second slide

- A point

In a Marp deck, Cmd+Shift+V (Ctrl+Shift+V on Windows and Linux) opens the Slide Sorter instead of VS Code's Markdown preview. The Markdown preview is still there with Cmd+K V (beside the editor), and shows a Marp deck as slides too: Scimax renders it with Marp Core, in the deck's theme, with scroll sync and double-click to the source (see Double-Click in the Markdown Preview). Other Markdown files preview as usual.

If you also have Marp for VS Code, it and Scimax both render decks in the Markdown preview; either works. Its markdown.marp.* settings are used when the matching scimax.marp.* ones are not set.

Double-Click in the Markdown Preview

VS Code's Markdown preview shows a Marp deck as slides (Scimax renders it; Marp for VS Code is not needed). Double-clicking in it jumps to the exact source line. The Slide Preview is the alternative that shows one slide at a time.

  1. Open a Marp deck (a Markdown file whose front matter has marp: true).

  2. Open the preview: Markdown: Open Preview to the Side (Ctrl+K V, Cmd+K V on macOS) or Markdown: Open Preview from the command palette. (In a Marp deck Cmd+Shift+V opens the Slide Sorter instead.)

  3. Double-click the text you want to edit.

The editor comes forward (or opens, if the file was not visible), the cursor moves to the start of that line, and the line is scrolled into view.

Where the cursor lands

The line is taken from the element under the pointer, not from its vertical position on the page, so it stays accurate in scaled slides.

Double-click onCursor goes to
A headingThe heading line
A line of a multi-line paragraphThat source line of the paragraph
A list item (at any depth)That item's line
A line of a fenced code blockThat line of code (not the fence)
A table rowThat row
Empty space on a slideThe slide's --- separator (file start for slide 1)

Links are left alone: double-clicking one does not jump to the source.

Reveal the Cursor in the Preview

This is for VS Code's Markdown preview (of a deck or any Markdown file); the Slide Preview follows the cursor by itself. The other direction: from the .md editor, run Scimax: Reveal Cursor in Markdown Preview (cmd:scimax.marp.revealInPreview) from the command palette. The preview scrolls so that the element on the cursor line sits at its top, and the editor scrolls the same line to its first visible row. If no preview is open, one opens to the side first. Focus stays in the editor.

It works for any Markdown preview, Marp or not. It uses the preview's scroll sync, so markdown.preview.scrollPreviewWithEditor must be on (the default).

The command has no default keybinding. To add one, open Keyboard Shortcuts and search for scimax.marp.revealInPreview.

Slide Preview

The slide preview shows the slide under the editor cursor, fitted to its tab, and follows the cursor as you move and type. Open it with the preview button in a Marp deck's editor title bar, or Scimax: Open Marp Slide Preview (cmd:scimax.marp.openSlidePreview). It opens beside the deck and leaves the focus in the editor.

The preview is the Slide Sorter tab in slide mode. The two buttons at the left of its toolbar switch between one slide and the grid of all slides (V does too), and the tab remembers the last mode.

Key or actionEffect
Move the editor cursorShows the slide under it
Left / Right, Up / Down, Page Up / Page Down, SpacePrevious / next slide
‹ and › in the toolbarPrevious / next slide
Double-click the slideCursor to the slide's first content line
V, or the grid and slide buttonsSwitch between one slide and the grid
Right-clickThe slide menu (see Editing Slides)

Hidden slides are shown (dimmed in the grid) so they can still be edited.

Citations in a deck are numbered, in the order they are first cited across the deck: [@key] shows as [1], [@a; @b, p. 4] as [1, 2, p. 4], and a bare @key (or {cite:t}) as Author [1], with the full reference as a tooltip. A source keeps its number on every slide that cites it. Each slide lists the numbered full reference of the sources it cites at its bottom, like a footnote, with the DOI or URL as a link. Other Markdown files keep author-year citations. Keys are looked up in bibliography:= in the deck's front matter (or a =myst.yml / Jupyter Book _config.yml above it), then in the bibliographies Scimax has loaded (scimax.ref.bibliographyFiles and the .bib files in the workspace), as the editor's citation hover does. A key found in neither is shown as typed and gets no footnote. See MyST Preview.

The list sits 40px above the bottom of the slide, inset 70px from each side, at half the slide's font size. A theme or a <style> block in the deck can move it (for example above a footer:) with CSS variables:

section {
  --scimax-refs-bottom: 70px;   /* distance from the bottom edge */
  --scimax-refs-inset: 60px;    /* left and right inset */
  --scimax-refs-size: 0.45em;   /* font size */
}

The slideshow (Present) and Marp exports (HTML, PDF, PowerPoint, images) render citations and each slide's references the same way. Marp CLI is given the citation plugins as its engine, along with the reference manager's entries for the keys the deck cites.

Slideshow

To present, Scimax makes Marp's HTML slideshow of the deck and opens it in the browser. Marp CLI builds it (see Marp CLI), and it is written to a private folder of the extension's storage, one per deck, so nothing is added next to the deck. Relative images still load from the deck's folder. Unsaved changes are saved first.

CommandStarts at
Scimax: Present Marp Slides (cmd:scimax.marp.present)The first slide
Scimax: Present Marp Slides from Current Slide (cmd:scimax.marp.presentFromCurrent)The slide under the cursor
Present from This Slide (thumbnail menu)The clicked slide

The play button in a Marp deck's editor title bar presents from the start, and so does x in the export menu (C-c C-e); c there starts at the current slide.

Key in the slideshowEffect
Right, Down, Space, Page Down, clickNext slide
Left, Up, Page UpPrevious slide
fFull screen
pPresenter view: current and next slide, notes and a timer, in a new window
Home / EndFirst / last slide

The four squares in the slideshow's control bar (bottom left, between full screen and the presenter view) open Marp's overview: every slide as a thumbnail. Click one to go there. It needs Marp CLI 4.3 or later; with an older one the button is missing (marp --version shows yours, and npm install -g @marp-team/marp-cli@latest updates it). In a presenter: true deck each thumbnail shows that slide's ink and sticky notes, and the slideshow's own ink comes back when you pick a slide. There you can also drag thumbnails to reorder the show, and right-click one to add, hide or move a slide (see Editing slides in the slideshow).

Hidden slides are left out of the slideshow, and its slide numbers count only the slides it shows.

A deck with presenter: true also gets a pen, a laser pointer, sticky notes and live Python cells in the slideshow; see Presenter Tools and Live Python. Because each deck always uses the same folder, ink drawn in one slideshow is still there the next time. When you Present again and the browser still has ink, sticky notes or slide edits from an earlier showing, a bar at the top of the slideshow asks whether to Keep them or Start fresh. Starting fresh clears them, but a copy stays in the browser's storage under marp-ink:<path>:before-load. A live reload or a browser reload does not ask, and Shift+C in the slideshow still clears the ink at any time.

Live reload

While the slideshow is open, saving the deck rebuilds it and the browser reloads at the slide you are editing (the one under the cursor when you save), so you can fix a slide and see it in the talk's layout without presenting again. Ink is kept. Edits are picked up only on save, and only for a slideshow started from this VS Code window.

The page checks for a new build once a second by loading a small <name>.html.version.js file written next to it, so it needs no server. Turn it off with scimax.marp.liveReload.

Present in an app window

To fill a window with the slides without taking over the whole screen (to share one window in a video call, or to present beside your notes), set

"scimax.marp.presentWindow": "app"

or, for one deck, add window: app to its front matter (window: tab turns it off for a deck when the setting is app):

---
marp: true
window: app
---

In an org file exported to Marp, #+MARP_WINDOW: app does the same.

The default is tab: the slideshow opens in a tab of your default browser. With app, Present opens it in a Chromium app window: no tabs, address bar or toolbar, just the slides, in a window you can size and move. Chrome, Edge, Chromium or Brave is found in the usual places; set scimax.marp.appBrowser to a path (or on macOS an application name such as Microsoft Edge) to choose one. If no browser is found, Present opens the slideshow in your default browser as before. Live reload, ink and the presenter tools work the same in an app window.

Presenting from an org file

A marp: link in an org file launches the slideshow when clicked (or with C-c C-o on it):

[[marp:talks/group-meeting.md][Group meeting slides]]
[[marp:talks/group-meeting.md::5][Start at slide 5]]

The path is relative to the org file (absolute paths and ~/ work too), and ::N starts at slide /N/. When the org file is exported, the link becomes an ordinary link to the .md file.

Hovering over the link shows a picture of the deck's first slide, the number of slides, and links to Edit the .md file, open its Preview beside the editor, or Present the slideshow (from slide N for a ::N link). The picture uses the deck's theme and inlines local images; remote images and web fonts are not loaded, so those may look different from the slideshow.

Directives

Marp directives set a deck's theme, pagination, headers and so on. Global directives go in the front matter. Local directives go in the front matter (for every slide) or in an HTML comment (from that slide on); with a leading underscore, <!-- _class: lead -->, they apply to that one slide.

In a Marp deck, Scimax completes directive names as you type them at the start of a front matter line or inside <!-- -->, then their values, and shows what a directive does when you hover over it.

DirectiveScopeValues offered
themeGlobaldefault, gaia, uncover, scimax, steampunk, cyberpunk, steel, steelpunk, and themes from scimax.marp.themes
paginateLocaltrue, false, hold, skip
headingDividerGlobal1 to 6
sizeGlobal16:9, 4:3
mathGlobalmathjax, katex
classLocallead, invert
transitionLocalThe HTML slideshow transitions (fade, push, ...)
header, footer, color, background*Local(free text; background* positions, repeats and sizes are offered)
style, lang, title, author, description, keywords, url, imageGlobal(free text)

Slide Thumbnails

The Slide Sorter is an editor tab that shows a thumbnail of every slide in the active deck in a grid, like a slide sorter in PowerPoint or Keynote. Open it with Cmd+Shift+V (Ctrl+Shift+V) in the deck, Scimax: Open Marp Slide Sorter (cmd:scimax.marp.openSlideSorter), or the layout button in the deck's editor title bar. It opens beside the deck, and like any tab it can be split, moved or dragged into its own window. It reopens after a reload.

The sorter follows the active Marp deck and keeps showing it while you work in its preview or in the sorter itself. (Earlier versions also had a Marp Slides view in the Explorer sidebar; the sorter replaces it.)

Double-click a thumbnail to jump to the text that defines that slide. The editor comes forward with the cursor on the slide's first content line and that line scrolled to the top. The slide's --- separator, the front matter (for slide 1), blank lines and directive comments such as <!-- _class: lead --> are skipped. A slide with no content puts the cursor on its separator. From the sorter, a deck that is not open in any group opens beside the sorter rather than replacing it.

In the thumbnailsEffect
Double-click a thumbnailCursor to that slide's first content line
Click a thumbnailSelects it
Drag a thumbnailMoves the slide (see Editing Slides)
Move the cursor in the editorBold number on the slide under the cursor
Right-click a thumbnailSlide menu (see Editing Slides)
Zoom slider, or + / - / 0Larger, smaller or default thumbnails
Pinch, or Ctrl + scrollZoom
Refresh buttonRe-renders the deck

Zoom

The slider in the toolbar at the top sets the thumbnail width, from 80 to 640 pixels. The thumbnails flow into as many columns as fit. The default is 240 pixels, and the sorter remembers the zoom. The keys + and - step the zoom and 0 resets it; they are plain keys because Cmd+/=Cmd+- zoom the whole VS Code window. A trackpad pinch (or Ctrl + scroll wheel) zooms too.

The thumbnails re-render as you type (after a short pause). Slide boundaries come from Marp itself, so ---, *** and ___ separators and the headingDivider directive all split slides exactly as they do in the preview, and a --- inside a code block does not.

The view renders with its own copy of Marp Core and follows these settings (see Configuration):

SettingEffect on thumbnails
scimax.marp.themesCustom theme CSS files (local paths only; URLs are skipped)
scimax.marp.enableHtmlAllows all raw HTML; otherwise Marp's safe allowlist applies
scimax.marp.mathTypesettingmathjax (default), katex or off

Run Scimax: Refresh Marp Slide Thumbnails (cmd:scimax.marp.refreshSlides) after editing a theme file, since theme files are not watched.

Editing Slides

The Slide Sorter can rearrange a deck without scrolling through the Markdown. Each change edits the .md file directly and is a single edit, so one Undo in the editor (or Cmd+Z in the sorter) reverses it. Only the lines that change are rewritten; the rest of the file is left exactly as it was.

Selecting slides

Click a thumbnail to select it, Cmd+click (Ctrl+click on Windows and Linux) to add or remove one, and Shift+click to select a range. Left and Right move the selection by one slide and Up and Down by one row of the grid (Shift extends it), Home and End jump to the first and last slide, and Cmd+A selects them all. Right-clicking a slide that is not selected selects just that slide, as in the Explorer.

Drag and drop

Drag a thumbnail to move its slide. A bar shows where it will land: between two thumbnails in the grid, or between two rows in a single column. Dragging a selected thumbnail moves the whole selection, in order, as one block, even when the selected slides were not next to each other. Dropping past the last thumbnail moves the slides to the end.

The slide menu

Right-click a thumbnail for these commands. They act on all selected slides when the clicked slide is part of the selection.

Menu itemCommandKey in the view
Edit with Claude Code…cmd:scimax.marp.slide.editWithClaude
Go to Sourcecmd:scimax.marp.slide.gotoSourceEnter (or double-click)
Reveal in Previewcmd:scimax.marp.slide.revealInPreview
Present from This Slidecmd:scimax.marp.slide.present
Cut Slidecmd:scimax.marp.slide.cutCmd+X
Copy Slidecmd:scimax.marp.slide.copyCmd+C
Paste Slide Aftercmd:scimax.marp.slide.pasteAfterCmd+V
Paste Slide Beforecmd:scimax.marp.slide.pasteBeforeCmd+Alt+V
Insert New Slide Beforecmd:scimax.marp.slide.insertBeforeCmd+Shift+Enter
Insert New Slide Aftercmd:scimax.marp.slide.insertAfterCmd+Enter
Duplicate Slidecmd:scimax.marp.slide.duplicateCmd+D
Move Slide Upcmd:scimax.marp.slide.moveUpAlt+Up or Alt+Left
Move Slide Downcmd:scimax.marp.slide.moveDownAlt+Down or Alt+Right
Hide Slidecmd:scimax.marp.slide.hideH (toggles)
Unhide Slidecmd:scimax.marp.slide.unhideH (toggles)
Export Deck(submenu of the export commands)
Delete Slidecmd:scimax.marp.slide.deleteDelete or Backspace

Reveal in Preview moves the editor cursor to the slide and opens the Slide Preview on it. Present from This Slide starts the Slideshow there. Export Deck lists every export (see Exporting Slides); it exports the whole deck.

On Windows and Linux use Ctrl in place of Cmd. Cmd+Z and Cmd+Shift+Z (or Cmd+Y) in the sorter undo and redo in the editor. These keys work only while the Slide Sorter has focus. They are handled by the view itself, so they are not listed in Keyboard Shortcuts and cannot be rebound. Right-clicking empty space below the slides offers Paste Slide After and Insert New Slide After, which add to the end of the deck.

How the Markdown changes

Copy and Cut put the slides' Markdown on the system clipboard, joined with ---, so you can also paste them into another deck or any file. Paste reads Markdown from the clipboard: text copied from the view comes back as the same slides, and any other text becomes one new slide.

Pasting slides with images

Slides often show images by relative path, such as ![bg](figs/a.png), which would break in a deck in another folder. So Copy and Cut write every relative path in the slides as an absolute one, and Paste makes them relative to the deck you paste into. Pasting back into the same deck gives the original paths. This covers Markdown images and links, <img>, <video> and other HTML src attributes, and url(...) in directives such as <!-- _backgroundImage: url(bg.png) --> and in <style scoped> blocks. Code and URLs are left alone.

When you paste into a deck in another folder and some images are outside that deck's folder, Paste asks what to do:

  • Copy the images into this deck's folder: an image under the source deck's folder keeps its path relative to the deck (figs/a.png goes to figs/a.png beside the new deck); any other image goes into figs/. A file already there with the same content is reused; a different file with the same name gets a numbered name (a-1.png).

  • Link to the original images: the paths point back at the originals.

  • Cancel paste.

Cut never moves or deletes images. Slides pasted with an ordinary Cmd+V in the editor keep the absolute paths, which work on your computer but not on someone else's.

Slide 1 never takes the front matter with it. Moving, pasting or deleting slides adds or drops --- separators as needed, so the first slide never starts with one and slides never merge. In a deck using headingDivider, a slide that starts with a divider-level heading gets no separator, since the heading already starts a slide. A slide's own directive comments (<!-- _class: lead -->) and speaker notes move with it.

Insert New Slide adds an empty slide (a --- line and a blank line) and moves the cursor into it, ready to type.

Hidden slides

Marp has no way to skip a slide, so Hide Slide wraps it in a comment that Scimax recognizes:

<!-- scimax-hidden
# Backup results
Details for questions.
scimax-hidden -->

Marp does not render the comment, so the slide is left out of the preview and of every export. Its thumbnail stays in the sorter, dimmed and marked hidden, and can be moved, copied or unhidden like any other slide. Any --> inside the hidden slide (such as its own directive comments) is written as -\-> so it cannot end the comment early, and Unhide restores it.

One side effect: Marp treats comments as presenter notes, so the hidden slide's text appears in the notes of the slide before it (for example in the presenter view of an HTML export).

The Marp Menu

Right-click in a Marp deck for the Marp menu. It acts on the slide under the cursor, or on every slide the selection touches. Each change is one edit, so one undo reverses it. All of these are in the command palette too, as Scimax Marp: ....

Slides

ItemCommandWhat it does
New Slide After This Onecmd:scimax.marp.newSlideAn empty slide after the current one, with the cursor in it
Split Slide at Cursorcmd:scimax.marp.splitSlideEverything from the cursor line on becomes a new slide
Duplicate Slidecmd:scimax.marp.duplicateCurrentSlideA copy after it
Move Slide Up / Downcmd:scimax.marp.moveCurrentSlideUpSwap with the slide before or after (also moveCurrentSlideDown)
Hide or Unhide Slidecmd:scimax.marp.toggleHideCurrentSlideSee Hidden slides
Delete Slidecmd:scimax.marp.deleteCurrentSlideFront matter stays

New Slide from Layout

Each adds a slide after the current one, with tab stops to fill in.

ItemCommandWhat it does
Title Slidecmd:scimax.marp.layout.titleCentered title, subtitle, author and today's date; no page number
Section Dividercmd:scimax.marp.layout.sectionCentered heading on inverted colors
Image Right / Image Leftcmd:scimax.marp.layout.imageRightText beside an image filling 40% of the slide (also layout.imageLeft); asks for the image
Full Imagecmd:scimax.marp.layout.fullImageAn image covering the slide; asks for the image
Two Columnscmd:scimax.marp.layout.twoColumnsTwo columns of text; needs HTML in slides, and offers to allow it
Quotecmd:scimax.marp.layout.quoteA large quotation with its source
Codecmd:scimax.marp.layout.codeA heading and a code block
Tablecmd:scimax.marp.layout.tableA heading and a table
Big Numbercmd:scimax.marp.layout.bigNumberOne large figure with a caption

Insert

Inserted at the cursor, as its own paragraph.

ItemCommandWhat it does
Image...cmd:scimax.marp.insert.imagePick a file, then inline, inline with a width, background, or left/right split, and an optional filter
Speaker Notescmd:scimax.marp.insert.notesA comment Marp shows as presenter notes
Fitted Headingcmd:scimax.marp.insert.fitHeading# <!-- fit --> Title, scaled to the slide width; on a heading line it makes that heading fit
Math Blockcmd:scimax.marp.insert.math$$ ... $$
Code Blockcmd:scimax.marp.insert.codeA fenced code block
Tablecmd:scimax.marp.insert.tableA three-column table
Build Listcmd:scimax.marp.insert.buildList* bullets, shown one at a time in the HTML slideshow
Python Cell...cmd:scimax.marp.insert.pythonCellA live python run cell (run, run auto or run hidden); turns on presenter: true if needed
Exercise Countdown...cmd:scimax.marp.insert.countdownA countdown box for an exercise on the slide; turns on presenter: true if needed

This Slide

Each sets a directive for just this slide, <!-- _name: value -->, at the top of the slide (or of every selected slide), replacing one already there. Choose (remove) or leave the text empty to remove it.

ItemCommandWhat it does
Class...cmd:scimax.marp.thisSlide.classlead, invert, or both
Background Color...cmd:scimax.marp.thisSlide.backgroundColorA CSS color
Background Image...cmd:scimax.marp.thisSlide.backgroundImageA file, as url(...)
Text Color...cmd:scimax.marp.thisSlide.colorA CSS color
Header... / Footer...cmd:scimax.marp.thisSlide.headerText at the top or bottom (also thisSlide.footer)
Page Number...cmd:scimax.marp.thisSlide.paginatetrue, false, hold or skip
Transition...cmd:scimax.marp.thisSlide.transitionFor the HTML slideshow
Scoped Stylecmd:scimax.marp.thisSlide.scopedStyleA <style scoped> block for CSS that applies only to this slide

✅ Deck

Each sets a key in the front matter, or removes it.

ItemCommandWhat it does
Theme...cmd:scimax.marp.deck.themeBuilt-in and custom themes, or a new custom theme
Size...cmd:scimax.marp.deck.size16:9 or 4:3
Page Numbers...cmd:scimax.marp.deck.paginateOn every slide
Header... / Footer...cmd:scimax.marp.deck.headerOn every slide (also deck.footer)
Heading Divider...cmd:scimax.marp.deck.headingDividerStart a slide at every heading of this level and above
Math Typesetting...cmd:scimax.marp.deck.mathmathjax or katex
Title, Author and Description...cmd:scimax.marp.deck.metadataAlso keywords and language; used by exports
Presenter Tools...cmd:scimax.marp.deck.presentertrue or offline: pen, laser, notes, live Python
Talk Timer...cmd:scimax.marp.deck.timerA countdown on every slide; turns on presenter tools
Check for Offline Usecmd:scimax.marp.checkOfflineLists what the deck would still fetch from the network

Theme

ItemCommandWhat it does
Choose Theme...cmd:scimax.marp.deck.themeAs in Deck
New Custom Theme...cmd:scimax.marp.theme.newSee Custom themes
Edit Current Themecmd:scimax.marp.theme.editOpens the theme's CSS file; for a built-in theme, offers a custom theme based on it
Add Deck Style Blockcmd:scimax.marp.theme.styleBlockA ~style:~ block in the front matter for small CSS changes

The scimax theme

theme: scimax dresses a deck in the blues of the crystal Scimax unicorn, with the amber of its horn, laid out like orgmode.org: a pale background, serif text, sans-serif headings in deep blue (level 1 and 2) and amber (level 3), quotes as grey-blue cards with a blue edge, and code in blues with amber names. Title and section slides sit on faceted crystal like the logo. It ships with Scimax, so every deck can use it with no theme file or setting: the thumbnails, the preview, the slideshow, every export and scimax marp build all know it.

---
marp: true
theme: scimax
paginate: true
header: Group meeting
footer: Your name · Your university
---
ClassUse it withDoes
lead<!-- _class: lead -->A centred title slide on pale, faceted crystal
invert<!-- _class: invert -->White on dark, faceted crystal, for section slides
starsclass: stars in the deckOrg's stars before headings: * for #, ** for ##, ...
  • A header is a deep blue band across the top, and a footer is a pale strip across the bottom.

  • <span class="todo">TODO</span> and <span class="done">DONE</span> colour a keyword: TODO in amber, DONE in grey.

  • Fitted headings (# <!-- fit --> Title) work.

  • A code block shows its language in a small tag in its top-right corner (Python, JavaScript, SQL, Plain text, ...), taken from the fence: ```sql is tagged SQL. Common languages are named; a fence with another language, or none, gets no tag. Live Python cells (```python run) have their own controls instead. All the bundled themes do this.

  • The fonts are ones orgmode.org uses when they are installed (Droid Serif, Noto Serif) and otherwise Cambria or Georgia, so nothing is downloaded and the deck looks the same offline.

To change it, choose New Custom Theme... and start from scimax: the new theme does @import 'scimax'; and changes only what you set. The CSS is in media/marp/themes/scimax.css in the extension. Marp for VS Code's own preview does not know the theme; Scimax's previews and exports do.

The steampunk, cyberpunk, steel and steelpunk themes

Four more themes ship with Scimax and work the same way as scimax: no theme file or setting needed, and the thumbnails, preview, slideshow, exports and scimax marp build all know them. All have the same lead and invert classes and the same todo / done spans as scimax, and fitted headings work.

---
marp: true
theme: steampunk   # or cyberpunk, steel, steelpunk
paginate: true
---
ThemeSlidesLook
steampunkAllAged parchment in a brass and copper frame, Victorian serif type (Baskerville, Didot), headings in oxblood and copper, quotes on riveted brass plates, code on dark iron, page numbers on brass gauges
leadA title framed in brass, with gears in the corners
invertBrass type on dark iron, with gears
cyberpunkAllA dark, scan-lined night in neon magenta, cyan and acid yellow, glowing uppercase headings, // before level 2, quotes and code in clipped-corner panels, [n/N] page numbers
leadA title over a neon horizon grid and setting sun
invertBlack on acid-yellow hazard stripes
steelAllIndustrial Pittsburgh: brushed steel and mill soot in black and gold, condensed signage headings, h1 in white-hot to red liquid steel, h2 over a molten bar, sparks off the top corner, quotes on steel plates, I-beam header, glowing page numbers
leadA title beside a white-hot pour into a molten pool, throwing a shower of sparks
invertDark type on flowing liquid steel
steelpunkAllLate-20th-century hardware: riveted gunmetal plate, stencilled military type, h1 in polished chrome, h2 over hazard stripes, quotes on bolted data plates, code on an amber CRT, tread-plate footer, UNIT n page numbers
leadA chrome title on diamond plate between hazard bands, beside a jet-turbine intake
invertBlack stencil on polished chrome, with a red stripe and rivets

The fonts are ones that come with macOS or are common elsewhere, with plain serif or sans-serif fallbacks, so nothing is downloaded and decks look the same offline. The CSS is in media/marp/themes/steampunk.css, media/marp/themes/cyberpunk.css, media/marp/themes/steel.css and media/marp/themes/steelpunk.css in the extension; to change one, choose New Custom Theme... and start from it.

Custom themes

New Custom Theme... asks for a name and a theme to start from (Marp's, scimax, steampunk, cyberpunk, steel or steelpunk), then:

  1. writes themes/<name>.css in the workspace folder (next to the deck outside a workspace), with /* @theme <name> */, @import '<base>'; and commented-out rules for the usual things to change;

  2. adds it to scimax.marp.themes (in the workspace settings);

  3. sets theme: <name> in the deck;

  4. opens the CSS beside the deck.

Uncomment and change the rules, and save: the thumbnails and preview update on every save of a theme file. The Marpit theme CSS reference lists what a theme can style.

Actions

The last part of the menu has Present, Present from Current Slide, Slide Preview, Slide Sorter, Edit with Claude Code and Export Deck, then Marp Help.

Editing Slides with Claude Code

With the Claude Code extension installed, right-click a thumbnail (or several selected ones) and choose Edit with Claude Code…. Claude Code opens with a prompt already filled in that names the deck, the slides and their lines, and ends with "Change: ", for example:

In the Marp slide deck talks/deck.md, edit slide 3 (lines 25-38). Change
only that slide and keep the rest of the deck as it is. Change:

Type what you want ("split this into two columns with the figure on the right") and send it. Claude Code reads the deck and edits it with its own tools, showing you the changes, and you can keep the conversation going. The thumbnails and preview update as the file changes.

In the .md file, Scimax: Edit Marp Slide with Claude Code (cmd:scimax.marp.editSlideWithClaude, also in the editor's right-click menu) does the same for the slide under the cursor, or every slide the selection touches. Unsaved changes are saved first, since Claude Code reads the file.

Claude Code has no command that takes a prompt from another extension, so this uses its vscode://anthropic.claude-code/open?prompt= link: the prompt is filled in but not sent, and it opens in a new Claude Code tab.

If a Claude Code tab is already open, the slides go to that chat instead: their lines are selected in the deck and added to the chat's input as an @-mention (for example @talks/deck.md#L25-38), and you type the change after it. Claude Code's link can only fill in a prompt in a new tab, which is why an open chat gets the @-mention rather than the full sentence. A chat open only in the Claude Code side bar is not detected, so a new tab opens.

Presenter Tools and Live Python

A deck can opt in to presenter tools in the HTML slideshow: a pen to draw on slides, a laser pointer, sticky notes, a spotlight, and Python code cells that run in the browser while you present. Add one key to the front matter:

---
marp: true
presenter: true
---

or choose Deck → Presenter Tools... in the Marp menu (cmd:scimax.marp.deck.presenter). Decks without the key are presented and exported exactly as before, unless you turn on scimax.marp.presenter: then every deck without a presenter: line gets the tools too, and a deck's own presenter: false still turns them off. Either way the tools are added only in a trusted workspace; an untrusted one gets the plain slideshow. The tools are added when you present (see Slideshow) and when you export HTML with h (see Exporting Slides). examples/marp-present/demo.md in the Scimax repository shows all of them, along with the rest of Scimax's Marp support: builds, transitions, notes, hidden slides, layouts, the Slide Sorter, the Marp menu and exports. Built copies run in the browser with nothing installed: the demo deck and the live Python deck (examples/marp-present/code.md).

To build a deck with the presenter tools from a terminal, a Makefile or Claude Code, use the CLI: scimax marp build talk.md --open (see Marp Slides in the CLI). It uses the same scripts and makes the same HTML as the h export.

Keys in the slideshow

KeyEffect
aAnnotate: edit mode with the pen on and the Annotate row; a again ends it (see Annotating)
1 to 5Pen colour while the pen is on: red, blue, green, black, yellow highlighter
rStroke eraser: drag over strokes to remove them whole; r again turns it off
R (Shift+R)Eraser that rubs out exactly the ink under it
zUndo the last change to this slide's ink (a stroke, an erase, a lasso move)
c / CClear this slide / every slide
lLaser pointer: a dot that follows the mouse
1 to 5Laser colour while the laser is on: red, blue, green, black, yellow
nNew sticky note at the mouse; type Markdown, click away to render it
hThis slide's Markdown in a panel that follows the slides, with a Copy button; h or Esc closes it
Right-clickA menu with all of these (Shift+right-click gives the browser's own menu)
Hold the mouse on a slideSpotlight: the screen darkens except a circle around the cursor (not on
buttons, links, code cells or widgets, while the pen or eraser is on, or while x picks a box)
sSave a standalone copy of the deck with the ink, <deck>-annotated.html
mSave the Markdown source with the ink as SVG, <deck>-annotated.md
SSave only the ink, as JSON
iLoad ink from such a JSON file (or drag the file onto the deck)
t / TStart or pause the talk timer / reset it to the full time
+ / -Add a minute to the talk timer / take one off
Alt+T (Option+T)Type a new time for the talk timer (20, 12:30, 90s), or add one
e / EStart or pause this slide's exercise countdown / reset it
b / wBlack / white screen, to pause for discussion; again, Esc or a click returns
xZoom: drag a box (or click) to magnify it; x or Esc zooms out
gGo to a slide: type a number or words from its title or text, then Enter
A number, then EnterJump to that slide (12 Enter goes to slide 12)
dEdit the slides' text and layout in place (see below)
N (Shift+N)A new blank slide after this one, opened for editing (see below)
vLive captions on or off (see below)
V (Shift+V)Get ready to record: opens the Microphone window; while recording, stop and save
M (Shift+M)The Microphone panel: microphone, level, gain, settings, Record/Stop (see below)
Cmd+P / Ctrl+PPrint; "Save as PDF" keeps every slide's ink as vector graphics

h shows how a slide is written: its Markdown, as in the deck (the first slide with the front matter), in a panel at the top right. The panel follows you from slide to slide, so you can walk through a deck and its source together; Copy puts the slide's Markdown on the clipboard. It needs the source in the HTML, which every deck built by Scimax has.

Marp's own keys (f full screen, p presenter view, arrows) still work, also after you click into a widget: the arrows, PageUp, PageDown, Home and End go on to the slideshow unless the widget uses them itself, or you are typing in one of its fields. Moving the mouse over a widget that fills the slide brings back Marp's buttons at the bottom. Its overview button (the four squares) works too, and each slide's ink and notes show on its thumbnail. A note's bar drags it; its ● picks a colour, – shrinks it to an icon and × deletes it, and right-clicking a note offers the same three. The right-click menu also has Fullscreen (f).

Talk timer

Give the length of the talk with a timer key and a small countdown sits in the top-right corner of every slide:

---
marp: true
presenter: true
timer: 20
---

A bare number is minutes; 20m, 45:00, 1h30m and 90s also work. Deck → Talk Timer... in the Marp menu (cmd:scimax.marp.deck.timer) sets it, and turns on the presenter tools if the deck does not have them (the timer is one of them).

A deck with presenter tools but no timer can get one during the show: right-click a slide and choose Add a talk timer..., or press Alt+T (Option+T on a Mac), then type the length and press Enter. It behaves like a timer from the deck, starting when you move to the next slide. It is kept for that slideshow in the browser until you right-click the timer and choose Remove the timer; the deck file is not changed, so add timer: to keep it for good.

  • It starts by itself the first time you move off the slide the show opened on. t (or a click on the timer) starts and pauses it; T resets it to the full time.

  • To change the time during the show, right-click the timer for a menu (Start/Pause, Reset, Add a minute, Take off a minute, Set time..., and Back to the deck's time), press + or - to add or take off a minute, or press Alt+T (Option+T on a Mac) and type a time (20, 12:30, 90s, 1h30m), then Enter. A time set this way keeps the timer running if it was, and T resets to it; the deck file is not changed.

  • It turns amber in the last 5 minutes (the last fifth of a short talk), red in the last minute, and after time is up counts the overtime as -1:05.

  • The audience window and Marp's presenter view (p), where the countdown sits next to Marp's clock, show the same time: pausing in one pauses both. The time is kept in the browser's local storage, so a reload, or a live reload when you save the deck, does not reset it.

  • After a rehearsal, press T before the talk. A timer untouched for 12 hours, or a deck rebuilt with a different timer, starts afresh.

  • It is not printed and is not in PDF, PowerPoint or image exports.

Exercise countdowns

A countdown fence puts a countdown box on a slide, for an exercise or a discussion. The first line is the time, the rest is the label (Markdown):

```countdown 3:00
Discuss with your neighbour: *which step is rate limiting?*
```

The time is written as for the talk timer: a bare number is minutes, and 3:00, 90s and 2m30s also work (the default is 5 minutes). Insert → Exercise Countdown... in the Marp menu (cmd:scimax.marp.insert.countdown) adds one and turns on the presenter tools.

  • e (or a click on the box) starts and pauses the countdown on the current slide; E resets it.

  • At zero it flashes red and reads "Time's up".

  • It runs in step in the audience window and the presenter view, and is kept across a reload.

  • The Markdown preview and the slide thumbnails show the box; PDF and PowerPoint exports show it with its full time.

Blank, zoom and go to a slide

  • Blank screen. b turns the audience screen black and w white, so the room looks at you instead of the slide. Press the key again, Esc, or click to bring the slide back. Pressed in Marp's presenter view, it blanks the audience window and the presenter view shows a reminder instead of going blank itself.

  • Zoom. x then drag a box over a dense figure or table to magnify that part to fill the screen (a click magnifies 2.5 times around that point). The audience window zooms too. x or Esc zooms out, as does moving to another slide. The pen still works while zoomed and the ink stays on the slide.

  • Go to a slide. g opens a list of slides by number and title. Type a number or words from a slide's title or text, use the arrows, and press Enter. Without the list, type a slide number and press Enter: 1 2 Enter goes to slide 12.

Live captions

v shows what you say as captions at the bottom of the slides, two lines at a time. Words still being recognised are grey. The captions fade out after a pause. Press v again to turn them off. "Captions at the top" in the right-click menu moves them out of the way of a slide's footer.

The captions use the browser's speech recognition. Chrome and Edge send the audio to their speech service, so they need a network connection. Firefox has no speech recognition. The first time, Chrome asks to use the microphone. The language comes from the deck's lang: directive (for example lang: de-DE), or from the browser's language if the deck has none.

Press v in the window you are working in. If you present from Marp's presenter view (p), press it there: the presenter view listens, and the captions show on the audience screen and, smaller, in the presenter view. Live reload keeps them on, but Chrome may ask for the microphone again.

Until the first words are heard, the caption bar says "Listening…". If something goes wrong, the bar says what it is instead: the microphone is blocked, there is no microphone, or the speech service can't be reached. The browser console (Cmd+Option+J) logs each problem with a [marp-captions] prefix. If the bar says "Listening…" but nothing appears while you speak, check which microphone Chrome uses: click the icon at the right of the address bar, or open chrome://settings/content/microphone.

Everything that is recognised is kept, with the time and the slide, so "Save transcript…" in the right-click menu writes it as Markdown, with one section per slide:

## Slide 3: Reaction mechanism

[00:04:12] The rate-limiting step here is the C–H activation.

The transcript grows over every talk given from the deck until "Clear transcript" empties it.

Recording the talk

The slideshow can record itself, the mouse and your voice as a video. V (Shift+V) does not start recording at once: it opens the Microphone window (see below), where you can check the microphone and its level, record a 5 second test, and get situated. Press ● Record there when you are ready. Chrome asks which tab to share: pick the slideshow. ■ Stop in the window, or V while recording, stops. Chrome then opens a save dialog, with <deck>-<date>-<time>.mp4 as the suggested name, so you choose the name and folder (Firefox downloads the video instead). If you cancel, "Save the recording…" in the right-click menu asks again, until the next recording. If the browser blocks pop-ups for the deck, V opens the panel at the top right of the slideshow instead, and ● Record there records this tab (the panel closes first so it is not in the video).

  • Format. Chrome and Edge write MP4 (H.264 video, AAC audio) that QuickTime and other players can play. Firefox writes WebM. The video is the size of the tab when recording starts, at most 1080p. If the window changes size later (for example going full screen), the picture is scaled to fit, with black bars. A tab only sends a new frame when something changes, so a talk made of slides gives a small file.

  • Sound. Your microphone is mixed with any sound the deck plays (videos, audio). Captions and the recording share one microphone, so Chrome asks for it once per page. The microphone, and how its sound is processed, are set in the Microphone panel (M, see below). Recording starts with a notice that names the microphone in use. If it records nothing for 15 seconds, a notice says so. Chrome does not remember the microphone permission for slideshows opened from a file, so it asks again after a reload.

  • The mouse. A recording of a tab does not show the mouse pointer, so while recording the slideshow draws a yellow ring around it, which shrinks when you click. It is hidden while the laser pointer (l) is on. Turn it off with "Highlight the mouse while recording" in the right-click menu.

  • Captions. Captions on the screen (v) are in the video. "Save captions of the recording (.vtt)…" also saves what was said during the recording as WebVTT subtitles, which video players and ffmpeg can use: #+begin_src sh ffmpeg -i talk.mp4 -i talk.vtt -c copy -c:s mov_text talk-captioned.mp4 #+end_src

  • Presenter view. V in Marp's presenter view opens the same Microphone window. While recording, the presenter view shows ● REC, the time and a small level meter for the microphone, and is not recorded itself.

Open the Microphone window and start recording before you go full screen: opening a window and the sharing dialog can take the browser out of full screen. The video stays in the browser until you stop. Closing the tab while recording asks first, and live reload waits until the recording is saved. If you stop with Chrome's own "Stop sharing" button, a "Save the recording…" button appears on the slideshow: Chrome only opens the save dialog after a click or key press, so click it to choose where the video goes.

The microphone

M (Shift+M), V, or "Microphone…" in the right-click menu, opens a panel to set up the microphone for recordings and captions, like the audio controls of a streaming app. The panel opens in a small window of its own, so it never covers the slides: put it beside them, or on the laptop screen while the slides are on the projector. M in the slideshow closes it again, as do Esc and × in the window. If the browser blocks pop-ups for the deck, the panel opens at the top right of the slideshow instead.

The window's top row has a ● Record / ■ Stop button for recording the talk. As from the presenter view, Chrome asks which tab to share: pick the slideshow. The window is not recorded, so it can stay open for the whole talk, showing the recording time and the level meter, and ■ Stop saves the video. While it is recording, closing the window asks you to stop first. In the window, V also starts and stops recording. When a recording was started in another window, the button is greyed out and says so. Settings changed in the window apply at once to captions and a recording in the slideshow.

ControlEffect
Record / StopRecords the talk (asks for the slideshow tab); shows the time while recording
InputWhich microphone: the system default or a named one. "In use" shows the one actually open
LevelA live meter of what is recorded, with a peak marker and a verdict: silent, quiet, good, too loud
Gain-12 to +24 dB, applied to the recording at once
LimiterHolds loud moments just under full scale, so they do not clip (on by default)
Voice filterCuts rumble below 90 Hz and some of a room's "boxy" build-up around 350 Hz (off by default)
Echo cancellationThe browser's: removes sound from your speakers, not the room's echo
Noise suppressionThe browser's: removes steady noise such as fans; can make a distant voice sound hollow
Automatic gainThe browser's: evens out the level, but also turns up the room between sentences
Test: record 5 sRecords five seconds through these settings and plays it back
DefaultsPuts the settings back (keeps the microphone)

The settings are kept in the browser and apply to every deck. Changing them during a recording or while captions are on carries on with the new settings. If the chosen microphone is unplugged, the system default is used.

Echo, or a voice that sounds "in a box". This is almost always the microphone being far from your mouth, so it records the room's reflections as much as your voice. Webcam, monitor and laptop microphones are like this; the panel warns when the microphone in use looks like one. No setting removes room echo after it is recorded. Choose a USB or headset microphone and put it a hand's width or two from your mouth. With a microphone that close, turning off echo cancellation, noise suppression and automatic gain often sounds more natural, and the voice filter can help a bit in a bare room.

Editing slides in the slideshow

For last-minute polish just before a talk, d lets you fix the slides in the slideshow itself: correct a typo, move a heading, make a figure bigger, or hide a line. A bar at the top says what is selected and what you can do. The edits stay in the HTML; they do not change the Markdown.

Key or actionEffect
dStart or stop editing (also in the right-click menu)
Double-click slideStart editing there: type in the text, or select an image, table, ...
ClickSelect a heading, paragraph, list item, image, table, code or widget
DragMove it (hold Shift to keep it level or upright)
Double-clickEdit its text; Enter or a click elsewhere finishes, Shift+Enter breaks line
EnterEdit the selected text
ArrowsNudge the selection (Shift: 10 px); with nothing selected, change slide
+ / -Bigger / smaller: text size, or the width of an image, table or code
Corner handleDrag to resize
DeleteHide it (it shows faintly while editing); Delete again shows it
Tab / Shift+TabSelect the enclosing block (the list, not the item) / back again
0Undo every edit to the selection
Cmd+Z / Ctrl+ZUndo (add Shift, or use Cmd+Y, to redo); the bar has Undo and Reset slide
EscClear the selection, then stop editing
tA new text box
N (Shift+N)A new blank slide after this one (also when not editing)
Cmd+B / I / UBold, italic, underline (Ctrl on Windows and Linux)
Cmd+/Strikethrough
Cmd+C / Cmd+XCopy / cut the selection (cut hides it, or removes an inserted element)
Cmd+VPaste a copied element, an image, or text (as a new text box)
+ Slide (bar)A new blank slide after this one, with its look; t adds text
Hide slide (bar)Leave this slide out of the show; on a slide added here, Delete slide
Hidden (bar)Pick a hidden slide to show it again
  • Edits are kept in the browser's local storage, so a reload keeps them, and Marp's presenter view shows them as you make them. You edit in the audience window, not the presenter view.

  • s saves the deck with its edits, ink and notes in one file, and opening that file brings the edits back. The JSON save (S) and load (i) include them too. m cannot put them in Markdown, and says so.

  • Edits belong to the content of their slide. If you change a slide in the Markdown and the slideshow reloads, that slide's edits are set aside rather than put in the wrong place, and a message says so. Slides you did not change keep theirs, even if they moved.

  • Text you paste while typing is plain text; maths in a paragraph stays as it is. Code cells, countdowns and widgets can be moved and resized but not typed in.

  • Printing (Cmd+P) shows the slides as edited.

  • N (Shift+N) adds a blank slide after the current one, whether or not you are editing, and opens it for editing: t adds a text box, Cmd+V pastes. It has the slide's look (theme, class, colours) but none of its content. Cmd+N is not used because browsers keep it for a new window and a page cannot take it over.

  • Adding, hiding, deleting or showing a slide reloads the page (Marp fixes its list of slides as the show opens) and comes back to that slide, still editing. The right-click menu has the same actions, also when you are not editing: Add a slide after this one, Hide this slide (Delete this slide for one added here), and Show hidden slide.

  • In Marp's overview (the four squares), drag a thumbnail to move that slide: a blue bar shows where it will go. Right-clicking a thumbnail offers Go to this slide, Add a slide after this one, Hide this slide (or Delete), Move to the start, Move to the end, and Show hidden slide. Slides hidden in the slideshow stay in the overview, greyed out with a dashed border where they were; click one (or right-click, Show this slide again) to bring it back. Each change reloads the slideshow and opens the overview again, except Add, which goes to the new slide, ready to edit. Slides hidden in the Markdown are not in the slideshow at all, so the overview does not show them.

  • Like the other edits, added, hidden and moved slides are kept in the HTML, not the Markdown: s saves them in the copy of the deck, and Marp's presenter view shows them too. Ink and notes stay with their slide; speaker notes and page numbers follow the new order. Ink on an added slide cannot go in annotated Markdown (m). To keep a slide for good, add it to the Markdown (or use Hide Slide there, see Hidden slides).

  • Sticky notes still work while editing: drag one by its bar, click it to type in it. The pen, eraser and spotlight are off while editing, and ink strokes cannot be selected or moved.

To keep a change for good, make it in the Markdown too.

The editing toolbar

The bar at the top of the slide while you edit also adds things and styles text. + Text puts a text box in the middle of the slide, ready to type in (or press t). + Image opens a file picker; you can also paste an image from the clipboard with Cmd+V. Pictures larger than 1920 pixels are scaled down so the deck stays small. + Table shows a grid: pick the number of rows and columns and start typing. In a table, Tab and Shift+Tab move between cells, and Tab in the last cell adds a row. Inserted elements can be moved, resized and styled like anything else on the slide, and Delete removes them.

The font menu, size box (in slide pixels), B, I, U, S and the colour swatch act on the words you have selected while typing, or else on the whole selected heading, paragraph or text box. When you select words while typing, the same controls also appear in a small bar just above them.

Copy (Cmd+C) a selected element and paste it (Cmd+V) on this slide or any other: the copy keeps its text, size and style and lands where the original was (offset a little on the same slide). A list item or table cell is pasted as a paragraph. Cut (Cmd+X) does the same but hides a slide's own element (Delete shows it again) or removes an inserted one.

Images are kept in the browser's local storage with the other edits. That storage is limited (a few megabytes for all decks opened from files), so if it fills up, a message says so; press s to save the deck with its edits before reloading.

Annotating while editing

The bar's second row, Annotate, draws on the slide, so you can sketch on a blank slide (N adds one) without leaving edit mode. Pressing a in the slideshow starts edit mode with the pen on, so these tools are a click away; a again turns the tool off and leaves edit mode. (In the presenter view, which cannot edit, a turns the pen alone on or off.)

The slideshow's keys keep working while you edit or annotate: x zooms, g goes to a slide, b / w blank the screen, + / - change the talk timer, h shows the Markdown, and so on. Only the keys edit mode uses itself (t a text box, d, a, c, r, z, n, the arrows with something selected, ...) do their editing job, and Esc first closes a zoom or a blank screen.

ButtonEffect
✎ PenDraw on the slide
✏ PencilA soft, grainy pencil line that darkens where you go over it again
▬ HighlighterDraw wide, see-through strokes
⌫ EraserRub out exactly the ink under it; a stroke it crosses becomes separate pieces
⌫ StrokesRemove every stroke it touches, whole
◌ LassoCircle ink to select what is inside (part of a stroke too); drag to move, Delete to remove
Colour swatchesThe drawing tool's colour; with a lasso selection, recolour it
Colour boxAny other colour
SizeThe line width (or the eraser's size), remembered per tool; with a lasso selection, resize it
ClearThis slide's ink (c); z puts it back
Clear allThe ink on every slide (Shift+C); z puts each slide's back

Click a tool again to turn it off. While a tool is on, the mouse draws instead of selecting text and images. Cmd+Z (Ctrl+Z) undoes ink: a stroke, an eraser drag, a lasso move, a recolour, a resize or a delete. 1-5 pick the usual pen colours. Esc clears the lasso's selection, then turns the tool off; the next Esc stops editing. The other buttons and Done turn the tool off too.

This is the same ink as the pen (a): it is saved with s, printed, and cleared with c. A pen or highlighter stroke in one of the usual colours (keys 1-5) is stored as before, so older decks read it; other colours the pencil and the eraser (⌫ Eraser, R) need this version. The eraser rubs out exactly what is under it: a stroke it crosses becomes separate pieces, each keeping the eraser's edge, so the lasso picks up one piece and moving it does not bring the erased part back. The lasso cuts the same way: it selects only the ink inside its loop, so it can take part of a stroke, and both sides keep the loop's edge. Ink drawn later across the gap is not erased. A pencil line keeps its grain when part of it is rubbed out. All of this looks the same on screen as in saved and printed copies, and Cmd+Z takes an eraser drag back like any other stroke.

Keeping the ink

Ink and notes belong to the slide they were drawn on and are kept in the browser's local storage, so a reload does not lose them. Scimax presents a deck with presenter tools from the same private folder every time, so the ink is still there when you present it again. To keep the ink for good, move it to another machine or share it, press s: the browser downloads one HTML file with the theme, images, iframes, fonts, tools and ink inside. Open it anywhere, keep annotating, and press s again.

Live Python cells

Mark a Python code block with run to make it a cell you can edit and run during the talk:

```python run
import numpy as np
np.linspace(0, 1, 5)
```
FenceCell
```python runEditable, with a Run button
```python run autoRuns by itself the first time its slide is shown
```python run hiddenSetup code: not shown, runs before the first cell

Insert one with Insert → Python Cell... (cmd:scimax.marp.insert.pythonCell), which also adds presenter: true if the deck does not have it.

In the slideshow, click a cell to edit it. Shift+Enter (or ▶) runs it and leaves the editor, so the arrow keys change slides again without reaching for the mouse. Ctrl+Enter (Cmd+Enter on macOS) runs it and keeps editing, for when you want to change the code and run it again. Esc leaves the editor without running. PageUp and PageDown, which presentation clickers send, always change slides, even while you are editing a cell. ↺ restores the original code and ⟲ restarts Python. All cells share one Python session, like a notebook. Output shows printed text, errors, the value of the last expression and matplotlib figures.

Code that fails is useful for teaching, so errors stay in view. The cell shows the traceback with the failing lines of code (as Jupyter does), and the error message itself (e.g. NameError: name 'y' is not defined) also appears in red in the cell's bar. The output keeps up to 45% of the cell's height, even when the slide's CSS makes the editor taller (e.g. .pyc .cm-editor { max-height: 600px !important; }); the editor scrolls instead.

Input from the audience: input()

input() works in a cell: it shows the prompt and a text box under the cell. Type the answer and press Enter; the prompt and answer stay in the output, as in a terminal, and input() returns the answer (always a string). Esc cancels, which raises EOFError as Python does at the end of input. While the box has the focus, keys go to it (the arrow keys move the cursor, not the slide); PageUp and PageDown from a clicker still change slides, and the box waits for you when you come back. It works inside functions and loops too.

```python run
val: str = input("Enter your value: ")
print(type(val))
print("doubled:", 2 * float(val))
```

The cell has to pause Python while you type, which needs WebAssembly JSPI: Chrome and Edge have it (so does VS Code's Python panel). In a browser without it, input() stops the cell with a message saying so; set the value in the code instead. A run hidden setup cell gets no input (EOFError).

examples/marp-present/code.md has a slide for each kind of code: plain highlighted code, live cells (the last value, shared variables, hidden and auto cells, input(), a plot, %pip) and broken cells for teaching (NameError, SyntaxError, IndentationError, TypeError, an error inside a function, IndexError, KeyError, numpy, raise, try/except, a warning). Try the built copy in the browser.

Python is Pyodide, running in the browser. With presenter: true it is downloaded from the jsdelivr CDN when the deck opens, so running cells needs an internet connection (the rest of the tools work offline). To present without one, use presenter: offline (see Offline Python). Pyodide's packages (numpy, scipy, matplotlib, pandas, sympy, ...) load on import, and %pip install name in a cell installs pure-Python packages from PyPI. An import that fails is retried once by installing the package from PyPI (import pycse installs pycse). Python starts downloading in the background when the deck opens, along with the packages the cells import.

In PDF, PowerPoint and image exports a run cell is ordinary highlighted code, and a run hidden cell is left out.

Running cells in VS Code

Press C-c C-c in a run cell to run it while you write the deck, without presenting. The output shows in a Python panel beside the editor, from the same Pyodide the slideshow uses, so code that works there works in the talk.

CommandKeyEffect
Scimax Marp: Run Python Cell (cmd:scimax.marp.runCell)C-c C-cRun the cell under the cursor
Scimax Marp: Run All Python Cells (cmd:scimax.marp.runAllCells)Restart Python and run every shown cell in order
Scimax Marp: Restart Python (cmd:scimax.marp.restartPython)A fresh Python: variables and imports are forgotten
  • All cells of the deck share one Python, as in the slideshow. The run hidden cells run first, once, before the first cell you run.

  • Each output is labelled with its slide and cell number; click the label to go to the cell. Clear removes the output and Restart restarts Python.

  • input() shows a text box in the panel, as in the slideshow (Enter answers, Esc cancels).

  • The panel is for one deck at a time: running a cell of another deck restarts Python for that deck.

  • A presenter: offline deck runs from the Python files it carries, so this works without a connection too; otherwise Pyodide is downloaded when the first cell runs.

  • Cells run only in a trusted workspace. The cells are also in Insert → Python Cell... and the commands are in the Marp menu's Insert section.

  • C-c C-c in a plain ```python block (no run, no header arguments) also runs it in this panel, labelled with its slide and line, so you can try a code example without writing its output onto the slide. The block stays ordinary highlighted code in the talk. A block with header arguments (```python :results output) runs with Babel and writes its results into the deck (Code Block Execution).

  • C-c C-c elsewhere in a Markdown file keeps its usual meaning (run a Babel code block, toggle a checkbox).

Offline Python

Set presenter: offline instead of presenter: true (or choose "On, with offline Python" in Deck → Presenter Tools...) to put Python inside the deck, so its cells run with no internet connection:

---
marp: true
presenter: offline
---
  • When you present or export HTML (h), Scimax adds Pyodide and the packages the deck's run cells import (with their dependencies) to the HTML file. Pyodide is downloaded once per version and cached in the extension's storage, so later builds need no connection either.

  • Packages are found from the top-level import / from ... import lines of the run cells. To include a package that is only imported later, during the talk, import it in a run hidden cell.

  • The file grows by about 12 MB for Python alone, and to 35-45 MB with numpy, matplotlib and pandas. It loads in a few seconds from disk.

  • %pip install, and packages that are not part of Pyodide (for example pycse or seaborn), still need a connection; the deck tries to download them, and the cell shows an error when it cannot. The Output panel (Scimax Marp Export) lists the packages that were included and those that were not.

  • s keeps the embedded Python, so the annotated copy also runs offline.

  • In the slideshow of a presenter: true deck, right-click → Save a copy that works offline... saves <deck>-offline.html. That copy contains Python, the packages chosen as above (including any edits you made to the cells), and your ink, notes and edits. The browser downloads Python for it, so you need a connection while saving, but not afterwards. When you save that copy again with s, it keeps the -offline name.

Checking a deck for offline use

Scimax Marp: Check for Offline Use (cmd:scimax.marp.checkOffline, also Deck → Check for Offline Use in the Marp menu) builds the deck in a temporary folder and lists in the Problems panel what would not work without an internet connection, each on the line it comes from:

  • Python cells in a deck with presenter: true (Python is downloaded when the deck opens; use presenter: offline)

  • %pip lines, and imports of packages that are not part of Pyodide, in an offline deck

  • images and other files that are not found

  • images, videos and scripts loaded from the web, and fonts or styles a theme loads from the web (shown on the theme: line)

Presenting or exporting HTML (h) an offline deck runs the same check and says when it found something. The problems are cleared when the deck is closed or checked again.

Notes

  • The tools are added only in a trusted workspace, since the deck's HTML and Python run in the browser. In an untrusted workspace Scimax warns and shows the plain slideshow.

  • A deck with presenter: true is built with Marp CLI's --html option, so raw HTML in its slides is rendered. The slide thumbnails and previews allow all HTML for these decks too (in a trusted workspace), so an <iframe> widget shows there as well, whatever scimax.marp.enableHtml says.

  • Scimax passes its own markdown-it engine to Marp CLI (--engine), which replaces an engine set in a Marp CLI configuration file for these decks.

  • The fonts for KaTeX math are downloaded once and cached, so deck.html also typesets math offline. Fonts that a theme loads from the web (an @import of a font service) fall back to local fonts without a connection.

  • The browser files live in media/marpPresent/ of the extension; the Output panel (Scimax Marp Export) lists what was inlined and any missing files.

Video, Sound and Web Pages

A slide can hold anything a web page can: a movie, sound, a YouTube video, a live Google Doc, a whiteboard, a simulation. Write the HTML in the Markdown. Marp allows HTML in a deck with presenter: true (in a trusted workspace); for other decks turn on scimax.marp.enableHtml. examples/marp-present/multimedia.md in the Scimax repository has a slide for each of these.

<video src="media/talk.mp4" poster="media/talk.jpg" controls></video>
<video src="media/talk.mp4#t=95,120" muted controls></video>
<audio src="media/clip.m4a" controls></audio>
<iframe src="https://www.youtube-nocookie.com/embed/VIDEO_ID?start=90"
        width="768" height="432" allowfullscreen></iframe>
<iframe src="https://docs.google.com/document/d/DOC_ID/preview"
        style="width:100%;height:470px"></iframe>
  • Movie and sound paths are relative to the deck. #t=95 starts at 1:35 and #t=95,120 also stops at 2:00.

  • YouTube needs the /embed/VIDEO_ID form; a watch or live link does not play in a frame.

  • YouTube does not play inside a deck opened from a file (Present, deck.html): it wants to know the web page it is embedded in, and a file has none, so it shows "Error 153". In a presenter deck (presenter: true) the frame shows the video's thumbnail and a play button instead, which opens the video on YouTube in a new window at the start= time.

  • Moving to another slide pauses the movies, sound and YouTube or Vimeo players on the slide you leave (presenter decks).

  • A movie or sound with autoplay (autoplay muted loop for a background movie) plays while its slide is shown, from where it was paused, not from when the deck opens (presenter decks).

  • For a Google Doc, Sheet or Slides file, replace /edit... in the URL with /preview, and share it as "anyone with the link can view".

  • Sites such as Excalidraw, Desmos, PhET, 3Dmol.js and OpenStreetMap can be framed. A site that refuses (it sends X-Frame-Options) shows an empty box, so link to it instead.

  • Pages that draw on a canvas (Excalidraw, Desmos, PhET, 3Dmol.js, maps) look blurred or jagged in a plain iframe: the slide is laid out at 1280 x 720 and scaled up to the screen, so the page draws small and the browser stretches it. Draw the frame at 1.5 times the size and scale it back down; on a 1920 x 1080 screen it is then drawn at the screen's own resolution (the page's buttons look two thirds as big on the slide). In the deck's front matter: #+begin_src yaml

style: | .sharp { position: relative; overflow: hidden; } .sharp > iframe { position: absolute; top: 0; left: 0; width: 150%; height: 150%; border: 0; transform: scale(0.6667); transform-origin: 0 0; } #+end_src and on the slide <div class"sharp" style="height:470px">

=.

  • After clicking into a frame, click the slide outside it to get the arrow keys back.

  • s in the slideshow and the h export put the movies and sound in deck.html, so it plays offline. Each file is in it once, however many slides use it, and the page plays them from there when it opens. They are base64, a third larger than the files, so shrink long movies first: a screen recording shrinks a lot at 15 frames a second (ffmpeg -i in.mp4 -vf scale=1280:-2,fps=15 -crf 30 -tune stillimage out.mp4). Web pages stay online.

  • PDF and PowerPoint exports show a movie's poster and an empty box for a web page. Put the link under it for a handout.

Writing Slides in Org

You can write a talk as an org file and export it to a Marp deck. In the org file, C-c C-e m s writes talk.md next to talk.org. C-c C-e m S also opens the .md file, and C-c C-e m p starts the slideshow. Each export replaces the .md file, so edit the org file, not the deck.

CommandKeysEffect
cmd:scimax.org.exportMarpC-c C-e m sExport the deck
cmd:scimax.org.exportMarpOpenC-c C-e m SExport the deck and open the .md
cmd:scimax.org.exportMarpPresentC-c C-e m pExport the deck and present it

The exporter follows the conventions of ox-marp, an Emacs exporter by uliw. uliw suggested this feature and contributed its specification and examples in issue #77. Thank you! Scimax adds its own slide features: notes, hidden slides, countdowns, live Python cells, citations and background images.

How org maps to slides

OrgMarp
#+TITLE:, #+AUTHOR:, #+DATE:Front matter, and a title slide (class lead, no page number)
#+SUBTITLE:An italic line under the title on the title slide
Text before the first headingThe end of the title slide
* and ** headingsSlides (# and ##); headingDivider: 2
*** headingA section revealed in one step (<div data-marpit-fragment>)
**** and deeper headingsList items: * **Heading:** first paragraph
- and + lists* lists, revealed one item at a time
1. lists1) lists, revealed one item at a time
[X] checkboxes, term :: definition[x] checkboxes, **term:** definition
Blocks after a listMoved into the last item, so they are revealed with it
#+begin_src jupyter-pythonA python fence (see languageMap)
#+begin_notes or a :NOTES: drawerSpeaker notes (an HTML comment)
#+begin_countdown 3:00A countdown timer
Other special blocks, #+begin_center<div class="name">
#+begin_export marp (or md, html)Copied as is; other back ends are dropped
--------, an explicit slide break
\(x\), \[x\], $$x$$, LaTeX environments$x$ and $$ blocks
[[cite:&key]], [cite:@key]Pandoc citations ([@key]), numbered per slide
#+BIBLIOGRAPHY:, ~ links~ in the front matter
:noexport: tag, COMMENT headingsLeft out (and any tag in #+EXCLUDE_TAGS:)

Fragment sections (*** headings) are raw HTML. A deck with presenter: true (the default) always allows HTML. For other decks, turn on scimax.marp.enableHtml, or the section shows all at once.

Keywords

Keywords in the file override the settings.

KeywordFront matter / effect
#+MARP_THEME: gaiatheme: gaia
#+MARP_HEADING_DIVIDER: 1headingDivider: 1 (and shifts the levels above)
#+MARP_PAGINATE: nilpaginate: false
#+MARP_MATH: mathjaxmath: mathjax
#+MARP_PRESENTER: offlinepresenter: offline (nil for none)
#+MARP_SIZE: 4:3Any other MARP_X becomes x (camelCase)
#+MARP_FRONT_MATTER: key: valueA raw front matter line
#+OPTIONS: marp-title-slide:nilNo title slide
#+OPTIONS: marp-fragments:nilPlain - and 1. lists, no fragment sections
#+PROPERTY: header-args :exports bothDefault header arguments for source blocks

Slide properties and tags

Heading property or tagEffect
:MARP_FIT: tA fitted heading (# <!-- fit -->)
:MARP_CLASS: invert<!-- _class: invert --> on that slide
:MARP_<X>: valueAny other <!-- _x: value --> local directive
:hidden: tagA hidden slide (or :MARP_HIDDEN: t)

Source blocks

:exports works as in org: code (the default), results, both or none. Results in a #+RESULTS: block or : output lines are exported with both or results.

To make a live Python cell, add :run t (or :marp-run t). Use :run auto to run it when the slide opens and :run hidden to hide the code. For full control, :marp "python run auto" (or :python "python run", the ox-marp form) sets the fence's info string.

#+begin_src jupyter-python :run t
x = input("Your guess: ")
print(int(x) ** 2)
#+end_src

Images

A link to an image becomes a Marp image. #+ATTR_MARP: adds Marp's image options:

#+ATTR_MARP: :bg right:40% :width 300px
[[file:figures/plot.png]]

gives ![bg right:40% w:300px](figures/plot.png). :bg alone makes a background image, :height becomes h:, :alt sets the alt text and any other word (sepia, blur) is kept. Without #+ATTR_MARP:, the width and height of #+ATTR_HTML: or #+ATTR_ORG: are used. A #+CAPTION: becomes an italic line under the image.

Org export settings

SettingDefaultEffect
scimax.export.marp.themedefaultTheme
scimax.export.marp.headingDivider2Heading levels that start slides
scimax.export.marp.paginatetrueSlide numbers
scimax.export.marp.mathkatexkatex or mathjax
scimax.export.marp.presentertruetrue, false or "offline"
scimax.export.marp.titleSlidetrueMake a title slide
scimax.export.marp.titleSlideClassleadClass of the title slide
scimax.export.marp.fragmentstrueReveal lists and sections step by step
scimax.export.marp.nestBlocksUnderListstrueMove blocks after a list into its last item
scimax.export.marp.languageMap{}Extra language mappings for source blocks
scimax.export.marp.frontMatter{}Extra front matter for every deck

Exporting Slides

Press C-c C-e in a Marp deck (or click the export button in the Marp Slides view's title bar) for the Marp export menu. In other Markdown files C-c C-e still opens the Markdown (Pandoc) export menu, which is also m in this one. Exports are written next to the deck, and the deck is saved first. When an export finishes, a notification offers to open the file or show it in its folder.

KeyExportOutputNeeds
pPDF, with bookmarksdeck.pdfBrowser
nPDF with presenter notes as annotationsdeck-notes.pdfBrowser
hSelf-contained HTML slideshowdeck.htmlNothing
sPowerPoint, each slide a picture (keeps notes)deck.pptxBrowser
eEditable PowerPoint in the Marp theme (experimental)deck-editable.pptxBrowser and LibreOffice
dEditable PowerPoint through Pandocdeck-pandoc.pptxPandoc
gEditable PowerPoint for Google Slides(one of the two above)LibreOffice or Pandoc
iPNG image per slidedeck.001.png, ...Browser
tPresenter notes as textdeck-notes.txtNothing
mThe Markdown (Pandoc) export menu

For a deck with presenter: true, h also inlines the theme, images, iframes, fonts and the presenter tools into deck.html, so the one file works anywhere. Other formats show live Python cells as ordinary highlighted code and leave out run hidden cells.

Marp CLI

All exports except d run Marp CLI, the tool behind Marp for VS Code's own export. Scimax uses scimax.marp.cliPath if set, then marp on the PATH (npm install -g @marp-team/marp-cli). Failing both, it offers to run it with npx, which downloads Marp CLI the first time. It asks once and remembers the answer.

PDF, PowerPoint and image exports render the slides in Google Chrome, Microsoft Edge or Firefox, so one of them must be installed. Local images are included when the workspace is trusted (see scimax.marp.allowLocalFiles). The export follows scimax.marp.themes and scimax.marp.enableHtml, so it matches the thumbnails. Marp CLI's output is in the Scimax Marp Export output channel.

Which PowerPoint

There are three PowerPoint exports:

PowerPoint (s)Editable, Marp (e)Editable, Pandoc (d)
Looks like the previewExactlyMostlyNo: PowerPoint default or your template
Text editableNo (pictures)YesYes
Presenter notesYesNoYes
NeedsBrowserBrowser and LibreOfficePandoc

Marp's editable PowerPoint is experimental: complex themes (such as gaia) may fail or come out incomplete. If LibreOffice is missing, the error offers the Pandoc export or the picture PowerPoint instead.

The Pandoc export gives real text boxes and notes in PowerPoint's own layouts. Set scimax.marp.pandocReferenceDoc to a .pptx to use its layouts and styles instead. Scimax prepares the deck for Pandoc: every Marp slide (including those made by headingDivider) becomes one slide, presenter-note comments become speaker notes, directive comments and hidden slides are dropped, and Marp image keywords (bg, w:300, ...) are removed. A slide with both text and pictures gets two columns, with the pictures on the right (on the left for bg left), since Pandoc would otherwise move the text to a slide of its own. A title: in the front matter makes a title slide.

Google Slides

Google Slides has no import from Marp, but it opens PowerPoint files, and editable ones import best. The g export makes Marp's editable PowerPoint when LibreOffice is installed, otherwise the Pandoc one, otherwise (after asking) the picture PowerPoint. The notification links to Google Drive: upload the file (New > File upload), then open it with Google Slides.

Export settings

SettingDefaultEffect
scimax.marp.cliPath(empty)Marp CLI executable; empty means marp on the PATH, else npx
scimax.marp.browserPath(empty)Browser for PDF, PowerPoint and images; empty lets Marp find one
scimax.marp.libreOfficePath(empty)LibreOffice soffice for editable PowerPoint (e)
scimax.marp.allowLocalFilestrueInclude local images (--allow-local-files), trusted workspaces only
scimax.marp.pandocReferenceDoc(empty)PowerPoint template for the Pandoc export (d)

Configuration

SettingDefaultEffect
scimax.marp.themes[]Custom theme CSS files for thumbnails, preview and exports
scimax.marp.enableHtmlfalseAllow all raw HTML in slides
scimax.marp.mathTypesettingmathjaxmathjax, katex or off
scimax.marp.liveReloadtrueReload an open slideshow when the deck is saved
scimax.marp.presenterfalsePresenter tools for decks with no presenter: line
scimax.marp.presentWindowtabapp: Present in a Chromium app window (no tabs or toolbar)
scimax.marp.appBrowser(empty)Browser for the app window (empty: Chrome, Edge, Chromium or Brave)

Each falls back to Marp for VS Code's markdown.marp.themes, markdown.marp.html or markdown.marp.mathTypesetting when it is not set (html: "all" allows HTML, "off" does not; the older markdown.marp.enableHtml is read too, but Marp for VS Code now warns about it, so use markdown.marp.html). A theme file names itself with a /* @theme name */ comment, and a deck uses it with theme: name. The export settings are listed under Export settings.

Marp CLI configuration files

A deck set up for Marp CLI often keeps its themes and options in a configuration file beside it rather than in VS Code settings:

# .marprc.yml
themeSet: ./theme
html: true

Scimax reads the same file, so the thumbnails, the slide preview, the Markdown preview and exports show the deck the way Marp CLI renders it. It uses the nearest .marprc, .marprc.json, .marprc.yaml, .marprc.yml or marp key of package.json, looking in the deck's folder and then each folder above it up to the workspace folder. Paths in the file are relative to it.

KeyEffect in Scimax
themeSetTheme CSS files or folders (all .css files inside), added to scimax.marp.themes
htmltrue or false, used when scimax.marp.enableHtml is not set
options.mathkatex, mathjax or false, used when scimax.marp.mathTypesetting is not set

An html allowlist object is not supported, and JavaScript configuration files (marp.config.js, .marprc.js) are not run. Saving the configuration file updates the thumbnails. The Marp for VS Code extension does not read these files, so if it is installed and renders the Markdown preview, set markdown.marp.themes for it as well.

Double-click in the Markdown preview

Double-click in the Markdown preview follows VS Code's own setting for double-clicking in the Markdown preview. Current VS Code releases turn it off by default, so enable it once in your user settings:

"markdown.preview.doubleClickToSwitchToEditor": true
SettingDefaultEffect
markdown.preview.doubleClickToSwitchToEditorfalseSet to true to turn on double-click jumps

If double-clicking a slide does nothing, this setting is the first thing to check. It also turns on VS Code's own (approximate) double-click for ordinary Markdown previews.

How It Works

Slides in the Markdown preview

Scimax contributes a markdown-it plugin (markdown.markdownItPlugins, src/marp/marpPreviewPlugin.ts). For a file with marp: true in its front matter it parses and renders with Marp Core instead of plain markdown-it, adds VS Code's code-line class and data-line source line to each block (for scroll sync and double-click), and puts the deck's theme CSS in front. The preview script media/marp/marpPreviewMode.js then turns off VS Code's own Markdown styles for that preview, since they would leak into the slides, and media/marp/marpPreviewSlides.css lays the slides out. Marp Core's browser script (also a preview script) fits headings marked <!-- fit -->.

A deck is parsed by Marp's own markdown-it instance, not the preview's, so the plugin applies Scimax's MyST and citation rules (src/markdown/mystPreview.ts) to it too, plus src/marp/slideCitations.ts, which adds each slide's reference list; the slide preview does the same in renderDeck.

Double-click in the Markdown preview

Scimax contributes a preview script (media/marp/marpPreview.js) through the markdown.previewScripts contribution point. Marp already tags each rendered element with its source line (data-line). On double-click inside a slide the script:

  1. finds the nearest element carrying a line number;

  2. adds the number of source line breaks between the start of that element and the caret (and one for the opening fence of a code block);

  3. sends the preview's own didClick message with that line, so VS Code reveals it exactly as it does for its built-in double-click.

VS Code's built-in handler estimates the line by interpolating the click's vertical position between tagged elements, which drifts inside scaled slides. Scimax's handler replaces it for Marp slides only; ordinary Markdown previews are unchanged. If the preview's message channel cannot be reached, the built-in handler runs as before.