Urbit / Docs

Udon

Table of Contents

Udon: A Guide

Udon is a way to write content for the web. It's a minimalist markup language for creating and rendering text documents, with a Markdown-inspired syntax. It's integrated with our Hoon programming language, allowing it to be used as standalone prose in its own file or embedded inside a Hoon source file, in which case it will be parsed into a tree of HTML nodes using Hoon's XML-templating syntax, Sail.

This document is itself written in Udon, and its source file is of the Udon .udon extension. As such, we use some formatted text to make it clear what is and isn't code, and to display that code to you instead of actually using its semantics for formatting.

There are quite a few similarities between Udon and the CommonMark standard, but there are enough differences that you shouldn't rely on existing knowledge of the latter. Udon generally supports only one syntax for each type of HTML node it emits. Udon is also stricter than Markdown: some syntax errors will prevent the file from being parsed at all.

We provide a few simple examples here of how to publish Udon files using our web interface.

Getting Started

Before starting, make sure that your urbit is mounted to Unix

To host Udon and other kinds of files, your ship also has a web-server, called Eyre, that can be found at http://localhost:8080/ if it’s your first ship that’s running on the machine, http://localhost:8081/ if it’s the second ship on that same machine, and so on. If you can't find your ship on one of these for some reason, try the next one. When a ship starts, it will print a message like http: live (insecure, public) on 8081 to let you know what port it's using.

That server has access to source files located in /<your-urbit>/home/web and any sub-folders thereof (except for /home/web/pages, which has special rendering rules.) Your ship's %home desk is served by default. You can change which desk you're serving, however.

Everything in the /<your-urbit>/home/web/ directory, with the exception of /home/web/pages, knows how to interpret and files of .udon type. So, to experiment with UM, create a .udon file in that path, and then try to access it at http://localhost:8080/<yourfile> in your browser -- no .udon required in the URL. It's a good idea to copy-paste the following examples into your file to see how the code works.

Make sure that you have proper syntax throughout, and check often that it renders properly as you add more code. If you have parser-breaking syntax, then the default renderer won't tell you much.

Syntax

Front Matter

The first thing on your file is a chunk of code called the front matter. It contains metadata about your page, such as date, title, and position relative to sibling pages.

As an example, let's look at the front matter of this very page:

:-  :~  navhome/'/docs/'
        sort/'11'
        title/'Udon'
    ==
;>

What's going on here?

:-, the Hoon rune for creating a cell, creates a cell composed of the :~ Hoon rune for creating a list, and ;>, which is a Sail rune for Udon.

:~ creates a list out of the three elements: navhome/'/docs/', which indicates that navigating “home” will bring you to the urbit.org/docs section of the website; sort/'28', which assigns it the 28th position of the immediate section that contains it, the one you see in the sidebar; and title/'Udon', which gives the page its title. The == digraph terminates the list.

;> is a Sail rune that creates a node that contains everything that follows it, telling the parser that that text should be interpreted as Udon. It's not technically front matter, but you'll always find it following the front matter, since all Udon must come after this rune.

The categories listed in the example above -- navhome, sort, and title -- make sense to Eyre by default, but any arbitrary category can be used. The code below is valid front matter.

:-  :~  date/~2018.9.12
        author/~lodleb-ritrul
        music/'rad'
    ==

Note that ~2018.9.12, ~lodleb-ritrul, and 'rad' are all of of atom types: @da(date), @p (ship name), and @t (cord), respectively. Only atom types work here. Having music/"rad" would prevent the source file from parsing.

Headers

Headers in Udon begin the line with one or more # characters, 'followed by a single space. After that space comes the actual text to be displayed. The number of leading #s corresponds to the resulting HTML header-size element: # yields an <h1>, ## yields an <h2>, and so on, through <h6>. The header for this section is ### Headers.

Example:

#### Header (h4)

##### Header (h5)
Produces:

Header (h4)

Header (h5)

Italics and Bold

Enclosing text with _ will cause that text to appear italicized, using an <i> element.

Enclosing text with * will cause that text to appear bolded, using a <b> element.

Example:

To get their *attention*, you need that _je ne sais quoi_.
Produces:

To get their attention, you need that je ne sais quoi.

Line Break

A blank line is interpreted as a line break, creating a distinct paragraph. However, a newline on its own is merely interpreted as a space on the same line.

Example:

Here's the first line.

This second line is separated by two newlines, so it's a separate paragraph.

This line looks like a distinct paragraph, but...
It's only separated by a single newline, so it's included in the same paragraph.
Produces:

Here's the first line.

This second line is separated by two newlines, so it's a separate paragraph.

This line looks like a distinct paragraph, but... It's only separated by a single newline, so it's included in the same paragraph.

Backslash Line Break

A backslash at the end of a line inserts a line break (<br>) after that line. This contrasts with the normal udon behavior of converting newlines to spaces.

Example:

I wonder how long each line
will be if I put backslashes\
at the ends of the lines.
Produces:

I wonder how long each line will be if I put backslashes
at the ends of the lines.

Escape

A backslash directly before a word (with no spaces) will be interpreted as an escape character, causing it to be rendered raw.

Example:

Here is some *bold* text.
Here is some \*not bold* text.
Produces:

Here is some bold text. Here is some *not bold* text.

List

A line beginning with a - followed by a space is interpreted as an element of an unordered list (<ul>). Each line in an unordered list is prepended with a bullet point.

A line beginning with a + followed by a space is interpreted as an element of an ordered list (<ol>). Each line in an ordered list is prepended with a number corresponding to its position in that list.

New list elements are delineated by newlines beginning with list symbols of the appropriate type (- or +).

Just as with non-list text, text on a bare newline will appear on line before, separated by a space. A `\` is used to create a line-break that isn't the list itself and isn't a list. Both of these kinds of newlines must be indented by two spaces, or else the page will not parse.

Indentation is also used for creating nested sub-lists. Sub-lists of one kind can be used with parent lists of the other kind. Sub-lists can't be nested directly after either kind of aforementioned newline that isn't a list element; only lines that are themselves list elements can nest sub-lists.

A newline after a blank line is interpreted as a new paragraph in normal text. Between to list elements, however, a blank line is semantically equivalent to a bare newline. To separate two lists with blank space, create a blank line followed by a `\` on its own line.

Example:

- Eggs
  (cage-free)
- Milk
- Butter
- Chicken\
  Make sure that you get enough for the barbecue
- Bread
  + Ask the baker what's fresh
  + Buy the second-freshest batch
- Cereal

\
+ First, separate eggs into a bowl.
+ Then, add a splash of cream.
  - If you don't have cream, you can use milk, but cream is preferred.
    + If you don't have milk or cream, melt an eighth of a stick of butter on
      a skillet
    + Scrambled the eggs in the bowl.
    + Pour the eggs onto the skillet before the butter browns
+ Heat the skillet

+ Scramble eggs and put them into skillet

\
+ Cool
+ Alright

Produces

  • Eggs (cage-free)

  • Milk

  • Butter

  • Chicken
    Make sure that you get enough for the barbecue

  • Bread

    1. Ask the baker what's fresh

    2. Buy the second-freshest batch

  • Cereal


  1. First, separate eggs into a bowl.

  2. Then, add a splash of cream.

    • If you don't have cream, you can use milk, but cream is preferred.

      1. If you don't have milk or cream, melt an eighth of a stick of butter on a skillet

      2. Scrambled the eggs in the bowl.

      3. Pour the eggs onto the skillet before the butter browns

  3. Heat the skillet

  4. Scramble eggs and put them into skillet


  1. Cool

  2. Alright

There are two ways to create links, but both contain the text be hyperlinked in [] followed by the destination in ().

The first type is an inline-style link, which simply uses the full URL as its destination.

Example:

[I'm an inline-style link](https://www.urbit.org/docs/using/shell)
Produces:

I'm an inline-style link

The second type is a reference-style link, which uses a relative path, based on the current page's location. Instead of the full url, we use .. to fill in the all of the URL that the destination has in common with the origin page.

Example:

[I'm a reference-style link](../shell)
Produces:

I'm a reference-style link

If this page is located at https://www.urbit.org/docs/using/, then these two examples should have the same destination.

A link to a section of the same page is in the style of [text to be linked](#the-destination).

The destination for the anchor link is designated with the HTML element id, so we need to use some Sail. If there are two or more anchors with the same id on the page, you will arrive at the earliest instance of the the anchor.

Example:
Check out this [section](#the-destination) that we want to jump to.

Four score and seven years ago....

It was the best of times, it was the worst of times...

;=
  ;h2
    ;div(id "the-destination"): Here we are!
  ==
==
Produces

Check out this section that we want to jump to.

Four score and seven years ago....

It was the best of times, it was the worst of times...

Here we are!

Double Quote

Text enclosed in double quotes (") will be rendered with opening and closing quotes -- that is, and .

Example:

"Yes," he said. "That is the way with him."
Produces:

“Yes,” he said. “That is the way with him.”

Code Literal

There are two ways to show the literal text of code without running it: inline and block. Both highlight the contained text in another color.

Note that the backslash, due to its role as the escape character, can only be displayed in a code block literal and not as inline code literal. This is a bug.

\
Inline Code Literal

Enclosing some text in

`

characters will cause it to be displayed as code, inside a <code> HTML element with monospace font and highlighted with a different background color.

Using ` is useful when you want to designated only part of a line as code. Since this page is written in Udon, we've been using this operation throughout this guide to format text to distinguish code from prose.

Example:

`*[a 2 b c] -> *[*[a b] *[a c]]` is like lisp's `apply`.
Produces:

*[a 2 b c] -> *[*[a b] *[a c]] is like lisp's apply.

Also, using the ++ prefix before a word will cause the word to be rendered as code with the ++ displayed, since that's the standard notation for an arm in Hoon.

Example:

The ++add arm is used to sum two numbers.
Produces:

The ++add arm is used to sum two numbers.

Block Code Literal

By enclosing a block of text in ` on their own lines before and after the contained text, the text will be treated as a code block.

Most of our examples so far have used.

Example:

  ```
  (def Y (fn [f]
         ((fn [x]
            (x x))
          (fn [x]
            (f (fn [y]
                 ((x x) y)))))))
  ```
Produces:
(def Y (fn [f]
       ((fn [x]
          (x x))
        (fn [x]
          (f (fn [y]
               ((x x) y)))))))

Hoon Constants

Hoon has several syntactic forms for literals (numbers, strings, dates, etc.) that can be used in udon as well. Udon detects such code, and so these forms will automatically appear inside a <code> element like inline code.

Example:

~2017.8.29 \
0xdead.beef \
%term

produces:

~2017.8.29
0xdead.beef
%term

Horizontal Rule

Three or more hyphens, such as ---, on their own line produce an <hr> element, the 'horizontal rule'. This is rendered as a horizontal line the width of its containing paragraph.

Example:
Above the line
---
Below the line
-----
And below this line, too
Produces:

Above the line


Below the line


And below this line, too

Block Quote

A section of text with the first line beginning with > and a space, and each successive newline indented by two spaces yields a <blockquote> HTML element. This block quote can itself contain more Udon, including more block quotes to render nested levels of quotation. Inline italicization with _ does not work, however.

Blank newlines do not end the block quote, but a blank newline followed by an unindented line of text will end the quote.

Example:

> As Gregor Samsa awoke one morning from uneasy dreams, he
  found himself transformed in his bed into a *monstrous* vermin.

Quote break.

> _See_ the child.

  He is pale and thin, he wears a thin and ragged linen shirt.
Produces:

As Gregor Samsa awoke one morning from uneasy dreams he found himself transformed in his bed into a monstrous vermin.

Quote break.

See the child.

He is pale and thin, he wears a thin and ragged linen shirt.

Poem

A poem is a section of text with meaningful newlines. Recall that, normally in Udon, newlines are treated as spaces and do not create a new line of text. If you want to embed text where newlines are retained, then indent the text in question with eight spaces.

Example:
        A shape with lion body and the head of a man,
        A gaze blank and pitiless as the sun,
        Is moving its slow thighs, while all about it
        Reel shadows of the indignant desert birds.
Produces:

A shape with lion body and the head of a man,

A gaze blank and pitiless as the sun,

Is moving its slow thighs, while all about it

Reel shadows of the indignant desert birds.

Sail Expressions

It's possible to use Udon as an HTML templating language akin to PHP, ERB, JSP, or Handlebars templates. This facility derives in part from the support for embedding Hoon code inside the markup.

Sail is a domain-specific language within Hoon for creating XML nodes, including HTML. It can be used directly within Udon to provide scripting capability with Hoon and also to provide more precise control over the resulting HTML. The Sail guide is a good place to learn the specifics.

Example:

;=
  ;p
    ;strong: Don't panic!
    ;br;
    ;small: [reactive publishing intensifies]
  ==
==

Produces:

Don't panic!
[reactive publishing intensifies]

A Simple Udon Blog

Publishing using Udon is really easy. It uses the file extension .udon. Here we'll create a very simple blog and publish our first posts.

First, let's setup a desk to serve our posts from:

|merge %site ~your-urbit %home

Then let's mount that desk to unix:

|mount /=site=

You should now be able to find a /site folder inside of your pier directory (either comet/ or your-urbit/).

To begin serving files from this new desk use |serve:

|serve %site

First let's create the landing page for our blog. Create the file site/web/blog.udon:

:-  :~  anchor/'none'
    ==
;>

# Hello

This is a simple blog I built on Urbit.

;list(dataType "post");

Try viewing this file at http://localhost:8080. Note that 8080 might be 8081, 8082, and so on, depending if those ports were being used when you started your urbit.

The only special thing about this page is the anchor/'none' front-matter which simply turns off the Tree navigation. Normally Tree gives navigation controls for walking around the filesystem, but taking them away makes the page seem more like a landing page.

The page looks pretty boring now. Let's add our first post. From unix run mkdir your-urbit/site/web/blog; touch your-urbit/site/web/blog/post-1.udon. (Currently there's a bug that prevents empty directories from getting created, so we have to touch a file inside them first.) In blog/post-1.udon let's add some filler content:

:-  :~  navhome/'/blog'
        type/'post'
        date/'~2016.6.20'
        title/'My first post!'
    ==
;>

Is this a decentralized Medium?  Let's find out.

But first, how about a long quote from Paramenides:

> True, he said; and therefore when ideas are what they are in
  relation to one another, their essence is determined by a relation
  among themselves, and has nothing to do with the resemblances, or
  whatever they are to be termed, which are in our sphere, and from
  which we receive this or that name when we partake of them. And
  the things which are within our sphere and have the same names
  with them, are likewise only relative to one another, and not to
  the ideas which have the same names with them, but belong to
  themselves and not to them.

Good to get that out of the way.

You should be able to view this file at: http://localhost:8080/blog/post-1.udon.

You'll also notice that now the list of posts here: http://localhost:8080/blog has updated.

Here we use two pieces of front-matter worth noting: type and navhome.

  • type implies that the page has some special handling by Tree. When type is post we automatically put in the date, title and author when they're specified and add some styles.

  • navhome sets the path used by the home button (the circle in the top left). Since we want to send people back to the root of our blog, we set this to /blog

Adding posts is as easy as dropping Udon files into the blog/ directory. Pretty easy!

Blog comments

Now let's enable comments on a blog post.

First, turn comments on by editing the frontmatter in post-1.udon. Your frontmatter should look like this:

:-  :~  navhome/'/blog'
        type/'post'
        date/'~2018.6.20'
        title/'Blog comments'
        comments/'true'
    ==
;>

Now try loading your post here: your-urbit.urbit.org/blog/post-1. You should see a comments box at the bottom of the page.

Try posting a comment! When you do you should see the following in your console:

+ /~your-urbit/home/55/web/blog/post-1/comments/~2016.6.20..23.20.54..0b88/md
------------[0]
~your-urbit[tree]: receiving comments, ;join %comments for details

Comments are written into the filesystem in the comments/ folder relative to the page they're enabled on. When a new comment is posted a notification is also posted to the /comments :talk channel.

Use ;join to keep up with comments as they get posted:

;join ~your-urbit/comments

That's it! Easy.

A Static Site

Hosting a static tree of content is easy. Let's put together a really simple site for an imaginary Urbit meetup group.

We assume you have at least followed the beginning of our first examples and created a desk called site and mounted it to unix.

Note: below we use Urbit's implementation of Markdown, Udon. This markup language allows you to use snippets of Hoon code but not HTML. In particular, we use Sail, a subset of Hoon. This Sail is rendered as HTML when the file is served to a client. To learn about Sail, head over to the Sail section of the docs.

In site/web/meet.udon put:

:-  :~  navhome/'/meet'
        navdpad/'false'
        navpath/'/meet'
        navmode/'navbar'
    ==
;>

# Neo-Tokyo Urbit meetup

;[email protected]"https://ofheroesandcliches.files.wordpress.com/2014/06/neo-tokyo.png"(width "100%");

Hi there! We meet once a week at The Chat to work on Urbit projects
together.

You can find out more about the group on the [about](about) page and
follow our projects in [projects](projects).

This is our landing page. Here we're using a few new bits of frontmatter: navdpad, navmode and navpath. navdpad just turns off the nav arrows. navmode can be set to 'navbar' to switch to a horizontal top nav instead of a left bar. navpath sets the path to load the nav items from.

Let's create about.udon and projects.udon. From Unix use the technique from before to create a new directory: mkdir your-urbit/site/web/meet/; touch your-urbit/site/web/meet/about.udon. Then put the following in site/web/about.udon:

:-  :~  navhome/'/meet'
        navdpad/'false'
        navpath/'/meet'
        navmode/'navbar'
        title/'About'
    ==
;>

# About

## Mission statement

The existing framework cannot subdue the new human force that is increasing day
by day alongside the irresistible development of technology and the
dissatisfaction of its possible uses in our senseless social life.

## Members

Membership is open to anyone.  Just send a message to `~talsur-todres`.

- Shōtarō Kaneda
- Motoko Kusanagi
- Ishikawa
- Faye Valentine
- Spike Spiegel

And site/web/projects.udon:

:-  :~  navhome/'/meet'
        navdpad/'false'
        navpath/'/meet'
        navmode/'navbar'
        title/'Projects'
    ==
;>

# Projects

## Active

;list;

## Discussion

;div(class "mini-module")
    ;script(src "/~/at/lib/js/urb.js");
    ;script(src "https://cdn.rawgit.com/seatgeek/react-infinite/0.8.0/dist/react-infinite.js");
    ;script(src "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment-with-locales.js");
    ;script(src "https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.1/moment-timezone.js");
    ;script(src "/talk/main.js");
    ;link(href "/talk/main.css", rel "stylesheet");
    ;talk(readonly "", chrono="reverse", station="comments");
==

This page introduces a new thing: the :talk module. Inside of the mini-module div we load the scripts needed by the ;talk component. The ;talk component is a static component for displaying a feed from a talk channel inline. Here we're going to display the discussion from our active projects inline.

We'll need a few active projects though. From unix: mkdir your-urbit/site/web/meet/projects; touch your-urbit/site/web/meet/projects/bike.udon.

In site/web/meet/projects/bike.udon:

:-  :~  navhome/'/meet'
        navdpad/'false'
        navpath/'/meet'
        navmode/'navbar'
        title/'Kaneda's Bike'
        comments/'true'
    ==
;>

# Urbit on a Motorcycle

Discussion on getting urbit to power Kaneda's motorcycle.

Here we just use the comments frontmatter to enable comments on the page. Adding other imaginary projects is left to the reader.

Building a static site takes only a few minutes and a few Udon files. Have fun!