Contributing
Kova is open source under the GNU General Public License v3 and welcomes contributions. This page covers how to build Kova from source, how its Markdown syntax is organised, and how to submit changes.
Questions
For quick questions — "is this a bug or intended?", "would a PR for X be welcome?", general chat with the dev team — join #kova-md:matrix.org rather than opening an issue. Issues are still the right place for bug reports and feature proposals that need to be tracked.
Prerequisites
| Tool | Version | Install |
|---|---|---|
| Node.js | 18 or later | nodejs.org |
| Rust | stable | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \| sh |
| Tauri CLI | v2 | Tauri prerequisites for your platform |
Linux only — WebKit dependencies
Nix users
nix develop drops you into a shell with Rust, Node, and Tauri all ready — no manual prerequisite install needed. Linux only (x86_64-linux / aarch64-linux); webkitgtk doesn't build on Darwin.
Getting started
The development server provides hot module replacement for the React/TypeScript frontend and automatic recompilation for Rust changes.
Before opening a PR, make sure both of these pass:
Project structure
Kova/
├── src/ # React + TypeScript frontend
│ ├── components/ # UI components
│ ├── engine/ # Parser, layout detection, themes, export, sheet formulas
│ │ └── __tests__/ # Engine unit tests
│ ├── store/ # App and settings state
│ ├── hooks/ # Shared React hooks
│ ├── i18n/ # Translations
│ └── styles/ # CSS and design tokens
├── src-tauri/ # Rust backend (Tauri 2)
│ ├── src/
│ └── Cargo.toml
└── .github/workflows/ # CI and release automation
Running tests
Tests live in src/engine/__tests__/ and cover the parser, layout detection, theme loading, keybindings, and the !sheet formula engine.
Building a release
macOS universal binary
rustup target add aarch64-apple-darwin x86_64-apple-darwin
npm run tauri build -- --target universal-apple-darwin
Markdown syntax conventions
This is the part that matters most if you're adding a new feature — it's easy to accumulate one-off, ad-hoc syntax over time. Kova uses five distinct forms, each with a specific job. If you're adding a new directive, find which category it belongs to below rather than inventing a sixth.
All of this is parsed in src/engine/parser/, mainly markdownToSlides.ts.
1. Frontmatter (document-level metadata)
A YAML block at the very top of the file, applying once to the whole deck: title, author, theme, theme_overrides, aspect_ratio, date, logo, footer. Use this for anything that describes the document, not an individual slide.
2. Slide separator and structural markers (bare tokens)
A small, intentionally closed set of bare tokens, each alone on its own line:
| Token | Meaning |
|---|---|
--- |
Slide separator |
??? |
Everything after this becomes speaker notes |
\|\|\| |
Column break within a slide |
Bare tokens have no room for parameters, are easy to collide with legitimate Markdown a user is typing (a table row using \|\|\|, a line of dashes), and are hard to discover. Don't add new ones — use an HTML comment or a bang-directive instead.
3. Slide-level flags/overrides (HTML comments)
<!-- hidden --> and <!-- layout: NAME -->, each alone on its own line within a slide. These:
- carry no visible content of their own (stripped before rendering),
- toggle something about how the slide itself is treated,
- degrade gracefully — a plain Markdown viewer just sees a comment.
The pattern is <!-- key --> for a flag, <!-- key: value --> for a flag with a value. Prefer this form for new slide-level steering information (e.g. a future per-slide transition, a slide ID for intra-deck links) rather than inventing new bare tokens or frontmatter-like blocks mid-document.
4. Content directives (bang syntax)
!name[label](target), each alone on its own line, for things that render visible content in place: !youtube[label](url), !video[label](path.mp4), !poll[label](url), !progress[label](value), !ref[Author, Year. Title], !sheet/!let, and the parameter-less !toc. Use this form when the directive produces an actual slide element, as opposed to category 3, which only changes slide behaviour.
!caption[text] is a variant that never becomes its own slide element: it attaches to whichever image, Mermaid diagram, or math block it directly follows, merged in by the parser, and renders centred underneath that element. A !caption with no such element immediately above it is a compile error, same as a misplaced !sheet.
5. Template variables
{title}, {date}, {slide_number}, {total} — text substitution inside header/footer strings only (theme_overrides.header.text, .footer.text, or a theme's YAML). Not valid inside slide body content.
Adding a new directive — checklist
- Decide which of the five categories above it belongs to (there's no sixth).
- Add parsing to
preprocess()or the relevant extractor insrc/engine/parser/. - Strip the raw marker from rendered content — don't let it leak into the slide body.
- Add cases to
src/engine/__tests__/parser.test.ts. - Document the syntax in the README's Features list, and on this wiki's Markdown & Syntax page if it's a significant one. Consider adding an editor snippet (see
EditorPanel.tsx).
Code standards
- TypeScript strict mode is enabled — no
any, no implicit conversions. - UI components belong in
src/components/; pure logic belongs insrc/engine/. - Write tests for all engine functions.
- Maintain consistent indentation and naming with the surrounding code.
Submitting changes
- Fork the repository on GitHub.
-
Create a focused feature branch:
-
Make your changes and add tests for any new engine logic.
- Commit using
type(scope): summary(e.g.fix(toc): …,feat(sheet): …) — seegit logfor examples. Keep the summary focused on why, not what. - Open a pull request against
main.
Keep PRs focused
One feature or fix per pull request. Stacked PRs for related changes are welcome.
Reporting bugs / proposing features
Open an issue at github.com/KovaMD/Kova/issues describing the use case, not just the syntax or fix you have in mind. Include:
- Operating system and version
- Kova version (visible in Settings → Updates)
- Steps to reproduce
- What you expected vs. what actually happened
Attach your .md file if the issue is layout- or render-related. For a new syntax proposal, say which of the five Markdown syntax categories above you think it fits — that shapes the design more than the exact spelling does.
Adding a language
Kova's UI is translated via a per-locale strings file — see Translating for the full guide to adding or updating a language.