Skip to content

Markup language

Mudlark uses a simple system: start any line with a special character to tell it what that line is. You write in the left pane, and the right pane builds the organized note from those prefixes. You can change which characters do what in Settings.

Mudlark has 20 prefixes plus an escape character covering everyday note-taking and advanced features like math, timers, tables, footnotes, custom callouts, and metadata. Two inline notations, [[ ]] for note links and { } for media, timers, and inline metadata, sit alongside the prefixes. Toggle individual types on or off in Settings.
on this page

Start here

Five prefixes cover most notes on day one.

# Project Notes

+ Buy groceries

! Demo is at 3pm

* Bring the good coffee

- buy gr ← checks off “Buy groceries”

# starts a section. + adds a task. ! marks a line as important. * makes a bullet point. - checks a task off by name, and a few letters of each word are enough.

Every prefix needs a trailing space before its content. # Heading is a heading. #FFF is plain text. The same rule applies inside action lines: _ + task removes a task named “task”, while _ +task is plain text.

The other everyday prefixes (", @) are in the next section. All 20 prefixes are in the reference table at the end of this page.

The basics

These eight prefixes cover ordinary note-taking.

PrefixTypeDescriptionExample
#HeadingCreates a section heading. Everything below it groups under that heading on the right.

# Project Notes

Project Notes

+TaskAdds an unchecked task. Tasks float to the top of their section on the right.

+ Buy groceries

☐ Buy groceries

-DoneChecks off a task by fuzzy-matching its name. Content after the space is the task name to match. Use _ to remove items of any other type.

- buy gr

☑ Buy groceries

!HighlightMarks a line as important. Rendered bold and prominent on the right.

! Demo is at 3pm

Demo is at 3pm

"QuoteWraps a line in a styled blockquote with a left border.

" Simple things should be simple

“Simple things should be simple”

*BulletCreates a bullet point. Multiple bullets form a list under the nearest heading.

* Bring the good coffee

• Bring the good coffee

%NumberedCreates a numbered list item. Auto-numbered on the right side.

% First step

1. First step

@MediaEmbeds an image or video from a file path, URL, or dropped file.

@ photo.jpg

[image: photo.jpg]

Inline formatting

Within any line, you can add bold, italic, and inline code formatting. This works inside any text-bearing prefix type.

*italic text*

**bold text**

***bold and italic***

`inline code`

Inline code uses single backticks and renders in a monospace font with a subtle tinted background. Code spans never process formatting inside, so `*literal*` stays literal. Escape a delimiter with a backslash: \*, \`, or \\.

Formatting markers are stripped in the rendered view and in fuzzy matching, so **bold** matches bold when checking off tasks or searching.

Lists and indentation

Bullet lists use *, numbered lists use %. To nest a list, indent two spaces per level. Up to five levels of nesting are supported.

* First item

* Nested item

* Deep nested

* Second item

Escaping and comments

Two prefixes keep Mudlark from interpreting a line. \ makes the rest of the line plain text, so \ + Not a task renders as the literal text “+ Not a task”. / marks a comment, a private note that stays in the left pane and never appears in the rendered note.

Acting on what you wrote

These prefixes act on what you already wrote: check a task off, remove an item, move it somewhere else, search it. They all find their target the same way, so smart matching comes first.

Smart matching

When you check off a task with - or move something with >, you don’t need to type the full name. Each word you type just needs to match the beginning of a word in the original, in order.

+ go get groceries

- go g ← matches (“go” → “go”, “g” → “get”)

+ go get groceries

- go gr ← matches (“go” → “go”, “gr” → “groceries”, skips “get”)

This also works with filenames and paths. A file like images/daisy-pants-stereo.jpg is broken up at slashes, dots, and dashes, so _ @ pants is enough to match it.

The editor colors the line live as you type: green when it finds exactly one match, yellow when more than one item matches (ambiguous, so nothing happens), and red when nothing matches.

Checking off tasks

- looks for a matching task and marks it done. Content after the space is the task name to match.

+ Buy groceries

- buy gr ← checks off “Buy groceries”

In the rendered pane, clicking a task checks it off by writing a matching - line. Clicking again clears it. Cmd-click copies the task text instead.

Removing items and sections

To remove any other kind of item, use _ followed by the type’s prefix. Works with Heading, Task, Highlight, Quote, Bullet, Numbered, Media, Code, Math, Timer, Table, Footnote.

! Ship by Friday

_ ! ship ← removes the highlight

@ images/photo.jpg

_ @ photo ← removes the media

Use _ # name to remove an entire section and everything in it.

# Old Ideas

+ some task

! some highlight

_ # old ← removes the section and all its contents

Moving things around

The > character moves content between sections.

Move an item here

Add a type prefix and a fuzzy match. The matched item moves into the current section.

> + fix ← moves a task matching “fix” here

Send an item to a heading

Add @ and a destination heading.

> + fix @ Home ← moves the task to the “Home” section

Relocate a whole section

Use the heading prefix # inside the move to target a heading by name. Without a destination, the matched heading and its contents re-parent into the current section. With @ destination, they re-parent under that heading instead.

> # Backlog ← moves the “Backlog” heading-block into the current section

> # Backlog @ Active ← moves the Backlog heading-block under “Active”

A fourth form moves a whole named block. See Acting on a whole block.

Writing to another section

The . character creates a new item under another heading without moving your cursor. Write the content, then @ and the destination heading. An optional type prefix sets the item type; without one, the line is written as plain text.

. + eggs @ Shopping ← adds the task under the “Shopping” heading

. call the plumber @ Today ← adds plain text under the “Today” heading

> moves an existing item. . creates a new one. Without an @ destination, nothing is created.

Finding and replacing

The , character searches everything above it. Add a pipe to replace matches, a type prefix to target a specific type, or @ heading to scope the search to a specific section.

, search ← highlights all matches

, search | replace ← replaces all matches

, + search | replacement ← only searches tasks

, search | replace @ Notes ← scoped to the “Notes” section

, \+groceries ← backslash escapes the type prefix, so it searches for the literal text “+groceries”

Rules as barriers

The ~ character draws a horizontal rule. Add a label to center text on the rule: ~ Chapter 2. A rule is also a barrier. Completions and moves cannot cross it: a - task below a rule cannot check off a task above it. Use one to fence off finished work.

Blocks

Blocks group several lines into one unit: a task list with a title, a code snippet, a table.

Opening and closing a block

Double a block-capable prefix to open a block. Use the same doubled prefix again to close it. Single-prefix lines stay as typed-line shortcuts.

!!

Ship the feature

Update the docs

Tell the team

!!

Block-capable types: ++ !! "" ** %% `` == :: && // ^^.

Named blocks

Give a block a title by writing the doubled prefix followed by a name. Named blocks show up as groups on the right side, and you can check off, remove, or move the whole group at once.

++ Shopping List

milk

eggs

bread

++

-- Shopping marks all items done. > ++ Shopping moves the entire block.

Acting on a whole block

A single type prefix targets one item. Doubling the inner prefix targets a whole named block.

- milk ← completes one task

-- Shopping ← completes the whole Shopping block

The same doubling works inside action lines. _ + milk removes one item and _ ++ Shopping removes the block. > + milk moves one item and > ++ Shopping @ Home moves the block under “Home”.

These commands look upward from where you type and stop at the nearest ~ rule. If two blocks with the same name are both in reach, Mudlark does nothing rather than guess.

Composite blocks like code, tables, and math can only be targeted this way: by name, with the doubled form.

Container and composite blocks

Blocks come in two kinds. Inside a container block (++, **, %%), every line is still its own item. You can check off one task inside a ++ block with a plain - milk.

A composite block (!!, "", code, math, tables, media) is one unit. Its inner lines cannot be targeted on their own. To complete, move, or remove it, use its name with the doubled form.

Code blocks

A code block can carry both a language hint and a title. Write the language right after the doubled backtick (no space), then a space, then the title.

``swift Login flow

let user = signIn()

user.persist()

``

The language drives syntax highlighting. The title renders as a header on the rendered block. A bare ``swift opens an unnamed block. A bare `` opens an unnamed, untyped block.

Block hints

A few block prefixes accept a hint glued to the opener, with no space between them. Code takes a language: ``swift Login flow. Math takes an aggregator: ==sum totals. Tables take a format: &&csv. Other prefixes accept the bare doubled form with an optional name only.

Inside a code block or a tab-separated table opened with &&tsv, pressing Tab inserts a literal tab character. Inside any && block table, Tab moves to the next cell. Everywhere else, Tab indents and the line-type prefixes stay in charge of structure.

Tables

Start a line with & and use pipes to separate columns. The first row becomes the header, and additional & rows form the body.

& Name | Age | City

& Alice | 30 | London

& Bob | 25 | Paris

Consecutive & rows are grouped into a single table on the right side. A heading above the table gives it a title.

A bare & row splits on the separator from your Default table format setting, so CSV, TSV, and semicolon split accordingly (the default is mudlark pipes). An explicit hint on a doubled opener always wins: &&csv, &&tsv, &&markdown, or &&semicolon.

Cells evaluate inline embeds. A pipe inside an embed or a [[Note|Alias]] link stays in one cell.

Table formulas

Table cells can use formulas that read columns and rows. See the table formulas section for column totals, row lookups, conditions, dates, and editor help.

Math and units

The = prefix evaluates an expression. It needs a space before the expression, as in = 5 km to mi. A doubled== opens a math block, and {= 1 + 1} places a result inside a line.

See the math and formulas guide for operators, units, functions, variables, math blocks, inline calculations, and table formulas.

Timers

The : character starts a timer. A duration after the prefix counts down. A mode word glued to the prefix changes what the clock does: :repeat starts over, :alarm waits for a time or a date, and :since counts up from one. Mudlark can notify you when a timer ends. That is a setting, and it is off until you turn it on.

Only these three mode words are recognized. Anything else after the prefix, like :D haha or :wq, is plain text, and so is a mode word with nothing after it. The words stay English when you remap the timer prefix in Settings.

Countdowns

Write a duration after the prefix. Units are hours, minutes, and seconds, in long or short form, and a bare number is minutes. Text after the duration names the timer.

: 25m ← counts down from 25:00

: 5 standup ← five minutes, named standup

A timer can run several phases in a row. Separate them with commas and give each an optional name: : 25m work, 5m rest. In the rendered pane, clicking a timer resets it and starts it again.

Repeating timers

:repeat starts the timer over each time it finishes and cycles forever. Glue a count from 1 to 9999 to the word and it stops after that many cycles. While it runs, the rendered pane shows the cycle beside the phase name, as in 2 of 4.

:repeat 5m ← restarts every 5 minutes

:repeat4 25m work, 5m rest ← four cycles of work and rest, then stops

The count has to touch the word. With a space, as in :repeat 4 5m, the 4 is read as part of the duration.

Alarms

:alarm waits for a time of day, a date, or both. Until the last hour it shows the target in the muted style. During the last hour it counts down, and for a minute after firing it shows 0:00. A time on its own means the next time the clock reads that, today or tomorrow, so the alarm comes back every day. A date fires once, at the start of the day unless you add a time, and afterwards shows its target struck through.

:alarm 15:30 standup ← today at 15:30, or tomorrow once that has passed

:alarm 2026-12-25 ← the first moment of that day

:alarm 2026-12-25 09:00 Retro ← that morning at 9:00, named Retro

Clicking an alarm does nothing, and Restart timers leaves it where it is.

Counting up from a date

:since counts up from a date, a time, or both. With a date alone it shows Day N, and the anchor day is Day 1. With a time it shows the hours and minutes that have passed, and after 24 hours it shows the days as well. A time on its own means the most recent time the clock read that, today or yesterday.

:since 2026-06-01 ← Day 1 on June 1, Day 2 on June 2

:since 2026-06-01 09:00 Coffee ← hours and minutes since 9:00 that day, then days

:since 9:15 ← counts from the most recent 9:15

A since line never notifies and never brings up the full-pane timer view. A date or time in the future is an error.

Writing dates and times

Alarms and since lines take a date as YYYY-MM-DD, a time as H:MM on the 24-hour clock, or both with a space between, then an optional name. Nothing else is accepted in a note. Words like 3pm and tomorrow, and a time that does not exist, like 25:99, show an error in place of the clock that names the accepted shape.

The capture bar, the mudlark:// URL scheme, and the Shortcuts actions accept relative words and write the absolute form into the note. :since now becomes today’s date and the current time, and :alarm tomorrow 3pm standup becomes tomorrow’s date, 15:00, and the name. In the editor, typing :since and a space offers the current date and time as a completion.

Timer blocks and inline timers

Doubled :: opens a timer block, with an optional name: :: Workout. The mode words work on blocks too: ::repeat Workout, ::repeat4 Tabata, ::alarm Meetings, and ::since Habits. Each line inside is one timer, written without the prefix: a duration in a countdown or repeat block, a date or time in an alarm or since block. Every kind closes with a bare ::.

::alarm Meetings

15:30 standup

2026-12-25 09:00 Retro

::

Inside a sentence, the same forms are {: 5m}, {:repeat 1hr}, {:repeat4 30s}, {:alarm 15:30}, and {:since 2026-06-01}. See Inline embeds.

Notifications

Notifications are off by default. Turn on Notify when a timer finishes or an alarm fires, under Timers in Appearance settings. macOS asks for permission once. If you decline, the setting shows a link to System Settings, where you can allow notifications for Mudlark later.

A countdown notifies when it reaches zero, with Restart and Open note buttons. An alarm notifies at its time, once for a date and every day for a time on its own, with an Open note button. A repeat with a count notifies once, when its last cycle ends. A repeat without a count and a since line never notify. When the note is on screen in the front window, you hear the sound and no banner appears. Clicking a notification opens the note.

Custom callouts

Beyond the two built-in callouts (! highlight, " quotation), you can define your own. Each custom callout has a prefix character, a name, an SF Symbol icon, a color, a font weight (regular or medium), and an italic toggle. Configure them in Settings → Markup.

A custom callout supports the same forms as a built-in one: a single line, a multi-line block (named or unnamed), and a typed removal target (_ <prefix> name). Toggle a custom callout off in Settings to make its prefix render as plain text again. You can also change which character any built-in type uses in the same Settings pane.

Earlier versions shipped a built-in question type on ?. That prefix is unassigned now. To get question-styled lines back, create a custom callout on ?. It behaves the same way, block form included.

Metadata

The $ character at the start of a line attaches metadata to a note. Rows shaped like key=value become a chip block at the top of the rendered pane. A $ line without an = is a free-form note, rendered verbatim. Reserved keys like tags, due, and priority get typed values and route to native fields on export.

$ tags=lisbon, trip ← tag chip, matches in note browser filters

$ source=https://example.com/article ← clickable source link

$ remember the shipping address ← free-form note, rendered verbatim

See the metadata guide for the full set of reserved keys, value types, destination-scoped pairs, and metadata scoped to a section or item.

Prefix reference

All 20 prefixes in one table. The first eight are the everyday set from The basics.

PrefixTypeDescriptionExample
#HeadingCreates a section heading. Everything below it groups under that heading on the right.

# Project Notes

Project Notes

+TaskAdds an unchecked task. Tasks float to the top of their section on the right.

+ Buy groceries

☐ Buy groceries

-DoneChecks off a task by fuzzy-matching its name. Content after the space is the task name to match. Use _ to remove items of any other type.

- buy gr

☑ Buy groceries

!HighlightMarks a line as important. Rendered bold and prominent on the right.

! Demo is at 3pm

Demo is at 3pm

"QuoteWraps a line in a styled blockquote with a left border.

" Simple things should be simple

“Simple things should be simple”

*BulletCreates a bullet point. Multiple bullets form a list under the nearest heading.

* Bring the good coffee

• Bring the good coffee

%NumberedCreates a numbered list item. Auto-numbered on the right side.

% First step

1. First step

@MediaEmbeds an image or video from a file path, URL, or dropped file.

@ photo.jpg

[image: photo.jpg]

`CodeA line of code. Add a language hint right after the prefix for syntax highlighting.

`js console.log("hello")

console.log("hello")

=MathEvaluates a math expression inline. Unit-aware arithmetic across lengths, weights, times, and temperatures. Variables and constants (pi, e) included.

= 100 km to mi

62.14 mi

:TimerStarts a countdown timer. Phases separate with commas, as in 5m work, 5m rest. A mode word glued to the prefix changes the clock: :repeat starts over each time it finishes, :repeat4 stops after four cycles, :alarm 15:30 waits for a time or a date, and :since 2026-06-01 counts up from one. Mudlark can notify you when a timer ends, if you turn that on in Settings.

: 25m

25:00 ▶

&TableCreates a table row. Use | to separate columns. First row becomes the header.

& Name | Role | Status

Name | Role | Status

>MoveRe-parents an item to the current section by type and fuzzy match. Requires a type prefix. Add @ to send to a specific section (e.g. > + fix or > + fix @ Home). Double the type prefix (> ++ name) to move a whole named block instead of one item.

> + fix bug

[moved: fix bug]

.Write-toCreates a new item under an @ destination heading (e.g. . + milk @ Shopping). An optional type prefix sets the item type; without one, the line is written as plain text.

. + eggs @ Shopping

[added to Shopping]

_RemoveRemoves an item or section by type and fuzzy match (e.g. _ ! ship or _ # old). The inner type prefix needs its own trailing space. Double the inner prefix (_ ++ name) to remove a whole named block. That is the only way to target composite blocks like code, tables, or math.

_ ! ship

[highlight removed]

/CommentA private note that only appears on the left side. Never rendered on the right.

/ todo: revisit this later

(hidden)

~RuleDraws a horizontal rule. Completions and moves cannot cross it. Add a label to center text on the rule.

~ Chapter 2

——— Chapter 2 ———

,FindSearches content above. Add | replacement to replace all matches. Add @ heading to scope to a section.

, search | replace

[3 replaced]

^FootnoteDefines a footnote. Drop a bare ^ at the end of a word (no space) to attach a sentinel. On its own line, ^ text defines the footnote. Sentinels and definitions pair by source order.

^ The cat was orange.

¹ The cat was orange.

\EscapeMakes the rest of the line plain text. Bypasses all prefix parsing.

\ + Not a task

+ Not a task

$MetadataPer-note metadata surfaced as a chip in the rendered pane. Use key=value rows like $ tags=trip, $ source=https://… or free-form notes like $ remember the shipping address.

$ tags=lisbon, trip

[tags: lisbon, trip]