On this page ▾
Setup hooks
A setup/ folder changes how slide.ng draws code, math and diagrams, which keys and menu entries there are, and how the markdown is read. It works like Slidev's setup/ folder: one file per job, each file default-exports a function.
The folder can be in the deck's own folder, in a theme and in an addon. Every root may have the same file. The roots run weakest first (built-in theme, theme:, the addons in order, the deck), so the deck has the last word. How the answers of several roots are put together is written under each hook.
There are two kinds of hooks:
- Node hooks run while the slides are generated:
shiki,katex,transformersandpreparser. They are bundled with esbuild first, so they can be written in TypeScript and import their own files and packages. An error names the root and the file. - Browser hooks become part of the app:
main,root,shortcuts,context-menu,mermaid,monacoandcode-runners. They must be.tsfiles. Angular compiles them with the rest of the app, soimport type { ... } from 'slide-ng/...'and@angular/corework.
| File | Runs | The function receives | Returns |
|---|---|---|---|
setup/shiki.ts |
generating | {} |
Shiki options |
setup/katex.ts |
generating | nothing | KaTeX options |
setup/transformers.ts |
generating | nothing | { pre, codeblocks } |
setup/preparser.ts |
generating | { filepath, headmatter, mode } |
a list of extensions |
setup/mermaid.ts |
browser | nothing | Mermaid config |
setup/monaco.ts |
browser | monaco |
editor options, or nothing |
setup/code-runners.ts |
browser | nothing | { language: run } |
setup/shortcuts.ts |
browser | (actions, list) |
the list of shortcuts |
setup/context-menu.ts |
browser | items |
the list of entries |
setup/main.ts |
browser, at start | nothing | Angular providers |
setup/root.ts |
browser, at start | nothing | nothing |
setup/providers.ts |
browser, at start | the older way | an array of providers is the default export |
Shiki
setup/shiki.ts returns the options of Shiki's codeToHtml. As in Slidev, langs adds languages, transformers change the HTML of every block, and theme or themes choose the colours (themes wins). Options of later roots are put over earlier ones, and langs of all roots are collected.
Install the transformer package first (npm i -D @shikijs/transformers); the sample uses it.
// setup/shiki.ts
import { transformerNotationDiff } from '@shikijs/transformers';
export default () => ({
langs: ['rust', 'elixir'],
themes: { light: 'github-light', dark: 'github-dark' },
transformers: [transformerNotationDiff()],
});
KaTeX
setup/katex.ts returns KaTeX's options, such as macros, strict or trust. A later root replaces a key of an earlier one, and a macros object is replaced as a whole. displayMode and output are decided by slide.ng.
// setup/katex.ts
export default () => ({ macros: { '\\RR': '\\mathbb{R}' } });
Transformers
setup/transformers.ts returns { pre, codeblocks }.
preare functions that get{ s, slide, options }for each slide before it is read.sis a MagicString of the slide's markdown, andslidehasno,index,title,frontmatterandsource. They may be async.preCodeblockandpostCodeblockare accepted too and run afterpre, because slide.ng has one pass.codeblocksare functions that get{ code, info, lang, meta, slide, options }for each code block. A string they return replaces the block. It is Angular template text, so it may use components, and a{or}in plain text is written{and}.undefinedleaves the block to the next function, and finally to the normal highlighting.
// setup/transformers.ts
export default () => ({
pre: [({ s }) => s.replace(/\[\[\[(.*?)\]\]\]/g, (_all, text) => `<b>${text}</b>`)],
codeblocks: [({ info, code }) => (info.startsWith('count') ? `<p>${code.trim().length} characters</p>` : undefined)],
});
Preparser
setup/preparser.ts runs when the markdown file is read, before it is cut into slides. It returns a list of extensions of Slidev's parser: transformRawLines(lines) changes the array in place, transformSlide(content, frontmatter) returns new text, and transformNote(note, frontmatter). The extensions of all roots are used.
// setup/preparser.ts
export default () => [
{
name: 'shout',
transformRawLines(lines: string[]) {
lines.forEach((line, i) => {
if (line.startsWith('!! ')) lines[i] = `# ${line.slice(3).toUpperCase()}`;
});
},
},
];
Mermaid
setup/mermaid.ts returns Mermaid's config. It applies to every diagram, and a diagram's own options come after it.
// setup/mermaid.ts
export default () => ({ theme: 'base', themeVariables: { primaryColor: '#ff0000' } });
Monaco
setup/monaco.ts runs once Monaco is loaded, only for decks that have an editor. It gets the monaco module, so it can add themes or languages (the monaco.languages.typescript namespace is deprecated and has no types in this Monaco version), and the object it returns is merged into the options of every editor. It may be async.
// setup/monaco.ts
import type * as Monaco from 'monaco-editor';
export default (monaco: typeof Monaco) => {
monaco.editor.defineTheme('mine', { base: 'vs-dark', inherit: true, rules: [], colors: {} });
return { fontSize: 14, minimap: { enabled: true }, theme: 'mine' };
};
Code runners
setup/code-runners.ts returns a map from the editor's language id to a function. {monaco-run} editors of that language run the function instead of the built-in JavaScript runner. The function gets the editor's text and returns { lines: string[][], error?: string }, where each inner list is one output line.
// setup/code-runners.ts
export default () => ({
python: async (code: string) => ({ lines: [[await runPython(code)]] }),
});
Shortcuts
See Shortcuts. The roots are chained: each gets the list the one before returned.
Context menu
setup/context-menu.ts gets the entries of the right-click menu and returns the list to show. An entry is { icon, label, run, disabled? }, and icon is an icon class such as i-carbon:star. The roots are chained.
// setup/context-menu.ts
import type { ContextMenuItem } from 'slide-ng/setup';
export default (items: ContextMenuItem[]): ContextMenuItem[] => [
...items,
{ icon: 'i-carbon:link', label: 'Open the notes', run: () => window.open('/notes') },
];
Main
setup/main.ts returns Angular providers that are added to the application, so a deck can provide any token the app injects, add an initializer, a router feature or a service. setup/providers.ts still works: it default-exports the array itself.
// setup/main.ts
import { provideEnvironmentInitializer } from '@angular/core';
export default () => [provideEnvironmentInitializer(() => console.log('the deck started'))];
Root
setup/root.ts runs once when the app starts, in an injection context, so inject() works. Slidev runs it in the root component. It is the place for code that watches the whole app.
// setup/root.ts
import { inject } from '@angular/core';
import { Router } from '@angular/router';
export default () => {
const router = inject(Router);
router.events.subscribe(() => (document.documentElement.dataset['path'] = router.url));
};
Differences from Slidev
- Slidev's setup files use
define...Setuphelpers from@slidev/types. Here a plain function is enough, and types come fromslide-ng/.... setup/main.tsreturns Angular providers, where Slidev's gets the Vue app.setup/root.tshas no component to work on.- Shortcuts are
{ name, key, run, when }, where Slidev's are{ key, fn, autoRepeat }. Context menu entries are{ icon, label, run, disabled }. - Not available:
setup/vite.config.ts,setup/vue.ts,setup/routes.ts, a run context forsetup/code-runners.ts(it gets the text only), Monaco'smonacoTypesSource, and markdown-it plugins insetup/transformers.ts. Thecodeblocksfunction is the way to render a block yourself.