Each section is one task, from start to finish. For what a feature does in full, see the User guide.
Install the editor, and what it needs
Prerequisites
Kaivalya Editor runs on Linux, under Wayland or X11, and draws with the graphics card through Vulkan — any desktop from the last ten years has it. Everything else is optional: the editor opens without it, and the feature that needs it says so when you ask for it.
| You install | For | Without it |
|---|---|---|
| git | fetching, pushing, rebasing and switching branch in the git pane | the pane still shows what changed, diffs, commits and history — those are read without git — but the four buttons fail and say why |
| enchant-2 | Check spelling | the command says enchant-2 is missing |
| a hunspell dictionary per language you write in | Check spelling in that language | the command names the dictionary it wanted |
| JetBrainsMonoNL Nerd Font | the text’s default font | your desktop’s monospace is used instead, and the status bar says so once |
On Ubuntu or Debian:
sudo apt install git enchant-2 hunspell-en-us hunspell-fr
Add the hunspell-… package of every language you write in — hunspell-en-gb,
hunspell-de-de, hunspell-es… The language a document is checked in is its
front matter’s lang: (lang: fr), or the editor’s own language without one.
The font comes from Nerd Fonts:
download JetBrainsMono, put the JetBrainsMonoNLNerdFontMono-*.ttf files in
~/.local/share/fonts/, and run fc-cache -f. Any other font is a setting
away — see Change a setting and font_family.
Nothing else is needed at run time: the heavy preview, the formulae, the diagrams, the formatters and the emoji font are inside the editor.
Install it
From the package — Ubuntu 24.04 and later
Each release has a .deb beside its notes, kaivalya-editor_<version>_amd64.deb.
Download it, then:
sudo apt install ./kaivalya-editor_1.0.0_amd64.deb
apt installs what it needs with it, and suggests git and enchant-2. The
editor is then in your applications. sudo apt remove kaivalya-editor
removes it; your settings stay in ~/.config/kaivalya-editor/. The package is
built on Ubuntu 24.04: it installs there and on later releases, not on older
ones — for those, build it from the source.
From the release archive — any distribution
Each release also has kaivalya-editor-<version>-linux-x86_64.tar.gz and its
.sha256. Check it, unpack it, and run its installer:
sha256sum -c kaivalya-editor-1.0.0-linux-x86_64.tar.gz.sha256
tar xzf kaivalya-editor-1.0.0-linux-x86_64.tar.gz
cd kaivalya-editor-1.0.0-linux-x86_64 && ./install.sh
It installs for you alone, under ~/.local; PREFIX=/usr/local ./install.sh
with sudo installs it for everyone. Built on Ubuntu 24.04, it needs a C
library as recent as that one’s (glibc 2.39): any distribution from 2024 on.
From the source
The editor can also be built from its source, which takes a command and some patience the first time.
-
The build tools, once:
sudo apt install build-essential pkg-config cmake clang mold \ libwayland-dev libxkbcommon-dev libxkbcommon-x11-dev libvulkan-dev \ libasound2-dev libfontconfig-dev libssl-dev libzstd-dev curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y -
In a copy of the repository:
make installThe first build takes ten minutes or more; later ones are quick. It installs for you alone, under
~/.local— no administrator — with the launcher entry and the icon. Press Super and type Kaivalya.
make uninstall removes a build installed this way and leaves your settings in
~/.config/kaivalya-editor/.
Check that it is all there
- Spelling: open a markdown file and run Check spelling from the markdown pane. Underlined words mean enchant and the dictionary are found; a message in the status bar names what is missing.
- git: in a project that has a remote, press Fetch in the git pane.
- The font: the status bar says so at start if
font_familynames a font that is not installed.
Windows
Windows is not supported, and there is nothing to download for it. The code compiles for Windows, and a guide for trying it yourself — untested, with what is known not to work (no spell check, no Recycle Bin, no installer) — is in the technical documentation: Building on Windows.
Choosing a stylesheet
A rendered markdown document is HTML and a stylesheet. The editor ships three, and the default is GitHub’s own — but a document written for a house style should be read in that house style, and the whole point of looking at a preview is to see what somebody else will see.
So there are four ways a document arrives at a stylesheet, and they are in order: the first one that answers wins.
1. The one you pick, while you are reading
At the right of the footer is the name of the stylesheet the document is rendered with. Click it — or open the list at the top of the markdown pane — and a list opens with everything you can switch to:
- Automatic, which is no choice at all, and hands the question back to the file and its folder. This is where every document starts.
- The one that ships with the editor, chosen explicitly. This is not the same as Automatic: it overrules a document that names its own stylesheet, which is how you read somebody’s house style in plain type without editing their file.
- Everything in your styles folder, by name — the three that ship and anything you have dropped beside them.
- Every stylesheet in this project, by name, with its path underneath.
- Choose a stylesheet…, for one that lives outside the project — which is what one house style and twenty repositories looks like.
The choice belongs to the document, not the window: two tabs are two answers, and the tab you are not looking at keeps its own.
And it is remembered for that file. Pick a stylesheet, close the tab, open
the file again next week: it is still the one. The note lives in the project’s
session, beside what was open and where the cursor was — nothing is written
into your document, because a css: line is a statement everybody who opens
the file will see and that is yours to write. This is a note the editor keeps
about how you read it.
Two consequences worth knowing:
- Choosing Automatic forgets rather than remembering that you have no opinion. That row hands the question back to the file and its folder, and an editor that wrote “no opinion” down would go on overruling them for ever.
- It is per project. The session is where this editor keeps what you were
doing, and a file opened outside any project has no session to keep it in.
For something that travels with the file itself — to another machine, to
somebody else — the
css:line below is the mechanism.
2. The document’s own front matter
---
css: ./house.css
---
# The document
The path is relative to the document. It travels with the file, and anybody who opens the file can see it — which is what makes it the right place for “this document is rendered this way” rather than “I happen to be reading it this way”.
3. A rule for a whole folder
A project’s rules live in .kaivalya/project.toml, in a .kaivalya folder
at its root. A folder’s stylesheet is an entry there, by path:
[[css]]
path = "" # the whole project
css = "house.css"
[[css]]
path = "notes/" # this folder and everything under it
css = "notes.css"
The longest path holding a document wins, so a rule for one folder
overrides the one for the whole project. The stylesheet is looked for in
.kaivalya/styles/ first — the project’s own stylesheets — and then from the
project’s root; an absolute path is taken as written. A key this editor has
never heard of is ignored rather than refused, so a file written for a later
version still answers what this one can ask. The same file holds the
project’s autocommit.
The project’s own stylesheets live in .kaivalya/styles/. The switcher in
the footer lists them first, then yours, and one of yours with the same file
name is not listed: the project’s wins.
Only that file is read. A .kaivalya file — the shape this had before,
one in any folder with css: house.css lines — and .kaivalya-style are no
longer read; move what they said into project.toml and delete them. When a
project still has a .kaivalya file, the editor says so as it opens.
The editor will write one for you, with every key explained in comments: Project > Write the project file. Everything in it is commented out, so it does nothing until you uncomment a line — and a file already there is opened rather than replaced.
4. The one that ships
Nothing else said anything, so the document is rendered with the default: a name in your settings file, found in your styles folder.
markdown_stylesheet = "github-light" # the default
# markdown_stylesheet = "github" # the pair, which follows your theme
# markdown_stylesheet = "kaivalya" # the editor's own
The styles folder
~/.config/kaivalya-editor/styles/, beside your themes and on the same terms.
The editor writes what it ships into it on every start and never touches
anything else in there:
github-light.css, github-dark.css |
GitHub’s own, which is the default |
kaivalya.css |
the editor’s: a serif column, both palettes in one file |
GitHub’s, because that is where a markdown file is read. It began as
github-markdown-css —
GitHub does not publish the stylesheet its site is built from, and this is the
one generated from a page it rendered.
It is derived from that file rather than identical to it, and it is not kept in step with new releases of it. Three rules differ, each marked in the file with the number of the report that changed it:
- a list inside a numbered list asked for roman numerals, which the preview’s renderer draws as an empty box, so it asks for letters instead;
- three rules are added so a foldable callout’s triangle sits where it should and turns when the callout opens — GitHub has no such construct, so its stylesheet has no rule for one;
- one rule is added so an image alone in its paragraph is centred.
grep the file for F95, F102 or F130 and every hit is one of them. The
licence and the attribution are untouched and travel at the top of each file.
The column’s width and padding, which that stylesheet deliberately leaves out,
are supplied by the preview.
A name, not a path
markdown_stylesheet takes a name, and the name is looked for twice:
<name>-light.cssor<name>-dark.css, following your theme;<name>.css.
That is one rule for the two ways a stylesheet can follow a theme. GitHub’s is
two files, one per theme, because that is how it is published. The editor’s own
is one file carrying both palettes, selected by a data-theme attribute the
page sets. A stylesheet of yours works either way: drop house.css in the
folder and set markdown_stylesheet = "house", or drop the pair.
A stylesheet you wrote is used exactly as written. It is not adjusted for the dark theme, and it never will be — guessing at somebody’s colours is how a careful design becomes an unreadable one. That is also why GitHub’s ships as two files rather than as one the editor recoloured.
If the folder is not there
The editor carries a copy of all three inside the binary and uses it. A preview with no styling at all is a worse failure than twenty kilobytes in an executable, so the folder is what you edit rather than what the editor depends on.
When a rule points at nothing
The footer says so — the name, and not found beside it, in the colour it uses
for everything else that needs looking at. It does not quietly fall back to the
default, because a typo in a css: line that silently does nothing is a typo
nobody ever finds.
Pasting a screenshot
Take a screenshot with whatever your desktop gives you — on GNOME, Print and
then Copy to clipboard — put the cursor where you want it, and press
Ctrl+Alt+V. Or use Paste an image in the markdown pane’s Document section.
Two things happen:
- The image is written to a file beside your document.
- A markdown link to it is inserted at the cursor, with the file’s own name
as the alt text:
.
One undo takes both the link and your place in the document back, as with every
other command here. The file stays where it was written — deleting somebody’s
picture because they pressed Ctrl+Z is not a thing an editor should do.
Where the file goes
Beside the document, in a folder named by the setting:
paste_image_folder = "images"
The folder is created the first time you paste. Set it to "" and the file
lands beside the document itself, with no folder in the link — which is what
somebody keeping one note and one screenshot wants, and what nobody keeping a
hundred does.
What it is called
The document’s own name, and the lowest number not already taken: notes-1.png,
then notes-2.png, then notes-3.png.
Deliberately a count rather than a timestamp. A name like
notes-20260907-143012.png is one nobody can read in a link, and a second
screenshot never overwrites the first either way.
The extension follows what was actually on the clipboard — usually PNG, but JPEG and WebP arrive from some tools, and the file is named for what it is.
Dropping an image instead
Drag an image file from the file manager onto a markdown document and it is
linked at the caret — several at once, one a line, each with its name as the
alt text. An image already beside the document, or in a folder under it, is
linked where it is. One from anywhere else is copied into the same folder a
pasted one goes to, under its own name, cat-2.png if cat.png is taken.
Any other file dropped on the text — or an image dropped on a file that is not markdown — is opened in a tab.
When it will not
Each of these says so in the footer rather than doing nothing:
- There is no image on the clipboard. You copied text, or a file name, or nothing. Some screenshot tools put a path on the clipboard rather than a picture; drag that file in instead, or copy the image itself.
- The document has never been saved. There is no “beside it” yet. Save it first.
- The folder will not be written to. The message says which folder and why.
Making, renaming and removing
A new document
Ctrl+N, or File > New file. It opens a tab in the window you are
in, and it writes nothing: a document that has never been named has nowhere to
be yet. Ctrl+S asks where, and the dialog opens at the project’s root
(the folder the editor was started in, with no project open). Save as on a
file that already has a name opens beside it.
That is the difference between this and the explorer’s New file below, which has already been told where.
From the explorer
Right-click any row and you get four things: New file, New folder, Rename and Delete.
“Here” means what you would expect: inside a folder you clicked, and beside a file you clicked.
The prompt
The first three open the same small prompt: the project’s path, then one field holding a path relative to it, then Cancel and Create.
The two together are the exact path, so there is nothing hidden about where it is going. And because it is a field rather than a label, three things fall out of it at once:
- A wrong row costs a correction, not a restart. Right-clicked the wrong folder? Edit the path.
- Folders on the way are made. Type
notes/2026/today.mdand both folders appear with the file inside them. One pass. - The caret is already where you type. Creating, it sits after the folder.
Renaming, it covers the name and leaves the extension —
notes.mdbecomestodo.mdwithout retyping.md.
A new file opens once it is made, because that is what you made it for. A new folder does not — there is nothing to open — so it is revealed in the explorer instead.
When it says no
Each of these leaves the prompt open with what you typed still in it, so the fix is a keystroke rather than starting again:
- The name is taken. Nothing is overwritten and nothing is quietly opened — either would hide the fact that you meant to make something new.
- The path leaves the project.
../out of the folder you opened is refused, and so is an absolute path. The field is free text so that a mis-click is correctable; this is the other half of that. - The folder will not be written to. The message says which and why.
Renaming
Renaming can also move: the field holds a path, so changing notes.md to
docs/notes.md puts it in docs.
If the file is open, the tab comes with it — the tab, the window title and the restored session all follow. Nothing is saved as a side effect: a file you had unsaved changes in still has them, and is still marked modified.
Renaming onto an existing name is refused. That refusal matters more than the others: renaming a file over another one would silently destroy the second.
Deleting
It asks first, and the question names what it is about to remove — by path, and for a folder, how many things are inside it.
It goes to your desktop’s trash, not to rm. So a file manager’s Restore
puts it back where it was. There is no undo inside the editor, and there will
not be one — the trash is the undo.
Two things it will not do:
- A file on a different filesystem from your home directory is not deleted, and says so. Trashing across filesystems needs a trash directory on that filesystem, which this editor does not create. Refusing is the safe failure while there is no undo.
- A tab on a deleted file closes without asking whether to save it, which would be an absurd question about a file you have just thrown away.
Exporting as HTML
Export as HTML…, in the markdown pane’s Document section, writes the document as a standalone web page: your stylesheet, your headings, your tables, and your mermaid diagrams and maths drawn as real pictures.
It is the same page the heavy preview draws, so what you see there is what you get — unless you ask otherwise in the dialog it opens first.
The three questions it asks
They are questions about this export, so they are asked when you export rather than kept in your settings file. The dialog opens on the sensible answer to each and remembers nothing: an export you did not think about is the one that needs nothing and looks like the preview.
Pictures inside the page
Ticked — the default — turns every picture into part of the page itself. One file that really is one file: you can email it, move it anywhere, and it still works. It costs about a third more bytes than the pictures did.
Unticked copies them into a folder beside the page, at their own size, and a
field appears for the folder’s name (img unless you say otherwise). One file
plus one folder: what you want when the pictures are large, or when the page is
going into a site.
Maths and diagrams drawn by the browser
Unticked — the default — draws both here, as SVG, before the file is written. The page then needs nothing at all: no network, no script, no web font.
Ticked leaves the maths as TeX and the diagram as its own source, and the page loads MathJax and mermaid.js to draw them when somebody opens it. Tick it when:
- the maths has to be perfect. MathJax is the reference implementation of TeX layout — it is what GitHub runs — and the editor’s own renderer, honest about it, is not quite that good.
- the diagram needs something mermaid.js has and the editor’s renderer does not — a diagram type, a theme, an interaction.
- the page is going on a site that loads those scripts anyway.
| Unticked | Ticked | |
|---|---|---|
| Opens with no network | yes | no |
| Asks somebody else’s host for a script | no | yes |
| Looks like the preview | yes | no — better maths, a different mermaid |
| Code is coloured | yes | yes |
| Prints to PDF | yes | yes, once the scripts have run |
Code is coloured by the editor either way, and is the one thing the scripts are not asked to redo: the colours travel in the page, so a fence looks the same in the preview, in an exported file and on a printer.
Where those scripts come from is yours to choose, because it is a request your reader’s browser will make. These two stay in the settings file, since they are about every export rather than this one:
export_mathjax_url = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-svg.js"
export_mermaid_url = "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs"
Point them at your own server, an intranet copy, or a different version. The defaults are each project’s own CDN, pinned to a major version — a page you export today should not be redrawn next year by something nobody has seen. The MathJax default is its SVG build, which carries no web font, so the page prints as what it looks like.
One back button on each footnote
Unticked — the default — gives a footnote one arrow for each place it was mentioned, each saying which section it goes back to. That is everything markup alone can do, and it needs no script.
Ticked gives it a single arrow that returns you to the mention you clicked, which takes fifteen lines of script in the page. The arrow is still an ordinary link to the first mention underneath, so a reader with scripts off still lands somewhere sensible.
Worth ticking for a document whose footnotes are cited from several places, and leaving alone for one where each is cited once — where the two are the same arrow.
Stylesheet
The page starts with the stylesheet the document is drawn with, which the
line names. Click it for the others: the one that ships, the ones in your
stylesheets folder, and the .css files of the project — the list the markdown
pane’s picker shows. The choice is for this export only: the document goes
on being drawn as it was, and the next export starts from its own again.
The editor still fetches nothing. The URLs are written into the page; it is whoever opens that page whose browser goes and gets them. If the scripts never arrive, the page still reads: the formulae are there as TeX and the diagram is there as its own lines, which is the failure this shape was chosen for.
A whole project
Project › Export the project as HTML… asks the same questions — pictures,
the browser’s drawing, the footnotes’ arrows, the stylesheet — and then for a
folder. Every markdown file of the project becomes a page at the same path in
it: docs/setup.md is docs/setup.html.
- Links between pages keep working. A link to another markdown file of the
project, and a
[[wikilink]], points at that file’s page. - A link to anything else of yours is left as written, and the editor says
how many there are when it is done: a link to
report.pdfpoints at nothing in the folder it wrote, unless you copy the file there. - What git ignores is not exported, whatever the explorer shows, and neither is the folder you export into when it is inside the project.
- The stylesheet starts on as each document is drawn: each page gets the one its front matter, its folder’s rule or your own choice gives it. Choose one in the list and every page uses it.
Three things it does not do
It does not leave a trail back to your machine. The preview carries a <base>
pointing at the document’s folder so that  resolves while you
read. The export does not: that line would be useless to whoever opens it and
would tell them where the file lives on your disk.
It does not fetch anything. A picture at an http:// or https:// address
is left as that address — this editor makes no network requests, and an export
is not the place to start. It will load for anybody who has the internet, as it
does now. (The browser-drawn export writes two script URLs into the page, which
is the reader’s browser fetching them rather than this editor.)
It does not hide a missing picture. One that is named and not there stays named and not there, so the export shows a broken image rather than quietly dropping it. A gap you can see is a gap you can fix.
And PDF?
There is no PDF export, deliberately. Export the page and print it from a
browser: Ctrl+P, Save as PDF, and you get proper pagination, margins and
page breaks from something that has spent twenty years on the problem.
Change a setting
Settings are a text file, and every option in it says what it does.
For yourself
-
Editor › Settings file, or
Ctrl+,. It opens~/.config/kaivalya-editor/settings.toml, writing it first if you have none. -
Change a value, or uncomment an example line:
font_size = 18.0 soft_wrap = false todo_tags = ["TODO", "FIXME", "NOTE"] -
Save, and restart the editor: your settings are read when it starts.
A line you delete is simply the default again. Editor › Reset the settings
file puts the whole file back to the defaults and keeps yours beside it as
settings.toml.bak. A file with a mistake in it never stops the editor
opening: it starts on the defaults and says what was wrong.
For one project
-
Project › This project’s settings opens
.kaivalya/settings.tomlin the project, making it if needed. -
Write only what this project changes, with the same names:
# .kaivalya/settings.toml tab_size = 4 todo_tags = ["TODO", "FIXME", "HACK"] -
Save: it applies at once, and again whenever the project opens. Your own settings come back when you open another project.
An option the editor does not know is named in the status bar, and the rest still applies.
Which options there are
Every setting lists them all with their comment and default — the same text the settings file carries.
Add a theme
A theme is one JSON file. You make yours by copying one that ships, under another name.
For every project
-
Open the themes folder,
~/.config/kaivalya-editor/themes/. -
Copy the theme nearest to what you want —
kaivalya-dark.json, say — to a name that does not start withkaivalya-, such asforest.json. Thekaivalya-*files are rewritten each time the editor starts, so edits made to them would not last; yours are never touched. -
In your copy, change
"name"at the top, and the"name"of the theme inside"themes", to what you will call it —"Forest". -
Change the colours. Every key is explained in
kaivalya-theme.schema.jsonbeside it, and an editor that reads the file’s$schemaline — this one included — shows that explanation as you type. -
In your settings file (
Ctrl+,), set the theme by its name:theme = "Forest"
The folder is watched: saving the theme file recolours the editor at once.
The theme line in your settings applies the next time the editor starts.
For one project
Put the same file in the project, in .kaivalya/themes/. It is added to
yours while the project is open, and wins over one of yours with the same name.
To make the project use it, name it in the project’s own settings —
Project › This project’s settings — which apply when the file is saved:
# .kaivalya/settings.toml
theme = "Forest"
Anybody who opens the project gets its theme; it is in the project, so it goes wherever the project goes, git included.
See also
- Themes: what the shipped themes are and what every value paints.
- Every setting: the
themesetting.
Add a stylesheet
A stylesheet decides how a document looks in the heavy preview
and in an exported page. It is a plain .css file, used
exactly as you wrote it. Choose the scope you need.
For every document
-
Put
house.cssin~/.config/kaivalya-editor/styles/. To follow the light and dark theme, put two files instead:house-light.cssandhouse-dark.css. -
In your settings file (
Ctrl+,):markdown_stylesheet = "house" # a name, not a path
It applies from the next start. Until then — or to try it — pick it from the stylesheet name in the status bar, which lists everything in that folder.
For one project, or one folder of it
-
Put the CSS in the project’s
.kaivalya/styles/. -
Project › Write the project file writes
.kaivalya/project.tomlwith every key explained; add a rule:[[css]] path = "" # the whole project css = "house.css" [[css]] path = "notes/" # this folder, overriding the line above css = "notes.css"
The longest path holding a document wins.
For one document
Name it in the document’s front matter, relative to the document:
---
css: ./print.css
---
This one travels with the file: anybody who opens it gets the same stylesheet.
Which one wins
The one you pick in the status bar, then the document’s css:, then the
folder rule, then markdown_stylesheet. Choosing a stylesheet
explains each.
Add a template
A template is a markdown file. Its name is what you type after /.
Make one
-
Create the file:
- for every project, in
~/.config/kaivalya-editor/templates/—meeting.md; - for one project, in its
.kaivalya/templates/. On the same name, the project’s wins.
- for every project, in
-
Write what should be inserted. Four markers are filled in:
# Meeting — {{date}} Present: ## Decisions {{cursor}}{{date}}today, 2026-10-07{{time}}now, 14:32{{title}}the document’s file name, without .md{{cursor}}where the cursor is left; otherwise at the end Anything else between
{{ }}is left as written. -
In any markdown file, type
/meetingandEnter.
The folders are read when the palette opens, so a template is usable the moment it is saved — no restart.
Keep them in folders
Folders under templates/ become groups in the palette —
templates/rpg/pc.md is Templates › rpg › pc — and typing still searches
every template, wherever it is filed.
Two that ship
name.md and mail.md are written into your templates folder at first start,
holding a placeholder name and address: write your own over them and /name and
/mail insert them anywhere. journal.md is the template Today’s note
(Ctrl+Shift+J) starts from — daily_note_template names another.
See also
- The / palette for the details.