Configure and Extend the Parser

Kolibry parses your presentation file (e.g. slides.md) in three steps:

  1. A "preparsing" step is carried out: the file is split into slides using the --- separator, and considering the possible frontmatter blocks.
  2. Each slide is parsed with an external library.
  3. Kolibry resolves the special frontmatter property src: ...., which allows to include other md files.

Markdown Parser

Configuring the markdown parser used in step 2 can be done by configuring Vite internal plugins.

Preparser Extensions

WARNING

Important: when modifying the preparser configuration, you need to stop and start kolibry again (restart might not be sufficient).

The preparser (step 1 above) is highly extensible and allows to implement custom syntaxes for your md files. Extending the preparser is considered an advanced feature and is susceptible to break editor integrations due to implicit changes in the syntax.

To customize it, create a ./setup/preparser.ts file with the following content:

import { definePreparserSetup } from '@kolibry/types'

export default definePreparserSetup(({filepath, headmatter}) => {
  return [
    {
      transformRawLines(lines) {
        for (const i in lines) {
          if (lines[i] === '@@@')
            lines[i] = 'HELLO'
        }
      },
    }
  ]
})

This example systematically replaces any @@@ line by a line with hello. It illustrates the structure of a preparser configuration file and some of the main concepts the preparser involves:

  • definePreparserSetup must be called with a function as parameter.
  • The function receives the file path (of the root presentation file) and headmatter (from the md file). It could use this information (e.g., enable extensions based on the presentation file).
  • The function must return a list of preparser extensions.
  • An extension can contain:
    • a transformRawLines(lines) function that runs just after parsing the headmatter of the md file and receives a list of all lines (from the md file). The function can mutate the list arbitrarily.
    • a transformSlide(content, frontmatter) function that is called for each slide, just after splitting the file, and receives the slide content as a string and the frontmatter of the slide as an object. The function can mutate the frontmatter and must return the content string (possibly modified, possibly undefined if no modifications have been done).
    • a name

Example Preparser Extensions

Use case 1: compact syntax top-level presentation

Imagine a situation where (part of) your presentation is mainly showing cover images and including other md files. You might want a compact notation where for instance (part of) slides.md is as follows:


@cover: /nice.jpg
# Welcome
@src: page1.md
@src: page2.md
@cover: /break.jpg
@src: pages3-4.md
@cover: https://source.unsplash.com/collection/94734566/1920x1080
# Questions?
see you next time

To allow these @src: and @cover: syntaxes, create a ./setup/preparser.ts file with the following content:

import { definePreparserSetup } from '@kolibry/types'

export default definePreparserSetup(() => {
  return [
    {
      transformRawLines(lines) {
        let i = 0
        while (i < lines.length) {
          const l = lines[i]
          if (l.match(/^@cover:/i)) {
            lines.splice(i, 1,
              '---',
              'layout: cover',
              `background: ${l.replace(/^@cover: */i, '')}`,
              '---',
              '')
            continue
          }
          if (l.match(/^@src:/i)) {
            lines.splice(i, 1,
              '---',
              `src: ${l.replace(/^@src: */i, '')}`,
              '---',
              '')
            continue
          }
          i++
        }
      }
    },
  ]
})

And that's it.

Use case 2: using custom frontmatter to wrap slides

Imagine a case where you often want to scale some of your slides but still want to use a variety of existing layouts so create a new layout would not be suited. For instance, you might want to write your slides.md as follows:




---
layout: quote
_scale: 0.75
---

# Welcome

> great!

---
_scale: 4
---
# Break

---

# Ok

---
layout: center
_scale: 2.5
---
# Questions?
see you next time

Here we used an underscore in _scale to avoid possible conflicts with existing frontmatter properties (indeed, the case of scale, without underscore would cause potential problems).

To handle this _scale: ... syntax in the frontmatter, create a ./setup/preparser.ts file with the following content:

import { definePreparserSetup } from '@kolibry/types'

export default definePreparserSetup(() => {
  return [
    {
      transformSlide(content, frontmatter) {
        if ('_scale' in frontmatter) {
          return [
            `<Transform :scale=${frontmatter['_scale']}>`,
            '',
            content,
            '',
            '</Transform>'
          ].join('\n')
        }
      },
    },
  ]
})

And that's it.