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 23 prefix characters 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 and timers, 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 23 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

?QuestionFlags a line as a question. Rendered in italics on the right so open questions stay visible.

? Should we move the deadline?

Should we move the deadline?

"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

@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”

Removing items and sections

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

? Should we change the deadline?

_ ? deadline ← removes the question

! 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 a pipe 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 pipe, the matched heading and its contents re-parent into the current section. With a pipe, they re-parent under the destination 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 type prefix, the content, then a pipe and the destination heading.

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

> moves an existing item. . creates a new one. The pipe destination is required.

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. Outside those contexts, Tab still 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.

Math and units

The = character evaluates math expressions. Units flow through every operator, so you can add, subtract, and convert between compatible quantities.

= 42 * 1.21 ← 50.82

= 5 km + 3 mi ← 9.83 km

= 100 km to mi ← 62.14 mi

= x = 5 ← assigns 5 to x

= x * 2 ← 10

Operators and constants

Supports + - * / ^ (power, right-associative), parentheses, and the constants pi and e.

Functions

Take a list of values: sum, avg (also mean, average), min, max, count, median, range. Take a single value: sqrt, abs, floor, ceil, round (with optional precision, e.g. round(3.14159, 2)), product.

Most functions preserve the unit of their inputs. count always returns a unitless number. sqrt and product reject unit-bearing inputs.

= sum(10, 20, 30) ← 60

= avg(85, 92, 78) ← 85

= sqrt(144) ← 12

Math blocks

A doubled == opens a math block. Add a name after the prefix to label it. Variables defined inside are scoped to the block.

== Budget

5000 - 1500

5000 - 2000

==

Aggregating prefix

Glue a function name onto the doubled prefix to roll every line in the block into a single result. The footer shows the function, value, and count.

==sum

100

200

300

==

sum = 600 (3 values)

Combine both forms: ==sum totals labels the block and aggregates it. Any of the list functions above (sum, avg, min, max, count, median, range, product) can be used as an aggregating prefix.

Unit conversion

Use to to convert a quantity between compatible physical units: length, weight, temperature, and duration.

= 100 km to mi ← 62.14 mi

= 5 kg to lb ← 11.02 lb

= 20 °C to °F ← 68 °F

Chained conversions parse right to left, so = 5 km to mi to yd converts to miles, then to yards. A bare scalar with to has no source unit, so = 5 to km is an error.

Timers and loops

The : character starts a countdown timer. Write a duration after the prefix. Mudlark notifies you when it finishes. The ; character starts a looping timer that restarts each time it finishes.

: 25m ← counts down from 25:00

; 5m ← restarts every 5 minutes

A timer can run several phases in a row. Separate them with commas and give each an optional name: : 25m work, 5m rest.

Doubled :: opens a named timer block, like :: Workout. Timers can also live inside a sentence. See Inline embeds.

Custom callouts

Beyond the three built-in callouts (! highlight, ? question, " quote), 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.

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, and destination-scoped pairs.

Prefix reference

All 23 prefix characters 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

?QuestionFlags a line as a question. Rendered in italics on the right so open questions stay visible.

? Should we move the deadline?

Should we move the deadline?

"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

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

@ photo.jpg

[image: photo.jpg]

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

% First step

1. First step

`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. Notifies when done. Supports multi-phase timers (e.g. 5m work, 5m rest).

: 25m

25:00 ▶

;LoopStarts a looping timer that restarts automatically when it finishes.

; 5m

5: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 a pipe 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 an item of a target type under a destination heading. Requires a pipe destination (e.g. . + milk | Shopping).

. + 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.

_ ? deadline

[question 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]