Skip to content

Markdown and documents

Help pages, rules, terms of service and other text you show players can be written in markdown, the plain-text format used by GitHub, Discord and most note apps. Write it in any editor, or keep it in your repository and review it there.

SAGL shows markdown in:


What's supported

CommonMark, the standard markdown, plus GitHub's tables, strikethrough and bare links:

# Heading, ## Subheading, ### and so on

A paragraph with **bold**, *italic*, `code`, ~~struck out~~ and [a link](https://example.com).

- A list
- of things
  1. numbered
  2. too

> A quote.

| Command | What it does |
|---|---|
| `/rules` | Shows these rules |

---

![An image](images/map.png)
  • Images are client files: a path relative to the markdown file (images/map.png beside docs/rules.md is docs/images/map.png), or game:<txd>/<texture> for one of the game's. Images from the web aren't shown.
  • Links to https:// addresses open in the player's browser, after they confirm.
  • HTML in markdown is shown as text, not run, so a document can't change the UI around it.
  • Chat messages and dialogs keep to one line's worth of styling: bold, italic, code and links. Headings, lists and the like run together.

Keys: {key:ID}

{key:ID} shows the key the player has for an action as its icon, as they've bound it in Settings → Controls. If they rebind it, the document shows the new key.

Press {key:chat} to talk, and {key:engine} to start the engine.

ID is the action's id: one of yours (server.actions.register), one of a client script's, or one of SAGL's own (chat, chat-command, indicator-left, indicator-right, hazards). It works in every chat message and dialog, with or without markdown.


The document viewer

player.ui.showDocument(file: string, options?: ShowDocumentOptions): Promise<number>

Shows a markdown client file in a panel over the game: the title, the document (scrolling if it's long), and a row of buttons. Resolves with the index of the button pressed, or -1 if they closed it (Esc) or left.

Option Type Default Description
title string none Across the top.
buttons string[] ["Close"] Left to right. The last is the main one (Enter).
requireEnd boolean false The last button only works once they've scrolled to the end (or it all fits). "Read to the end to continue" shows meanwhile.
closable boolean true Esc (gamepad B) closes it, resolving -1.

One shows at a time; more wait their turn. player.ui.closeDocuments() takes down any they have showing or waiting.

Terms of service

The server decides what accepting means: let them play, kick them, or remember that they accepted.

server.on("playerJoin", async (player) => {
  if (await accepted(player)) return;
  const choice = await player.ui.showDocument("docs/terms.md", {
    title: "Terms of service", buttons: ["Decline", "Accept"], requireEnd: true, closable: false,
  });
  if (choice === 1) await rememberAccepted(player);
  else player.kick("You need to accept the terms to play here.");
});

Freeroam's /rules shows client-files/docs/rules.md this way.


Styling

The theme's stylesheet, /sagl/ui/theme/markdown.rcss, styles markdown everywhere SAGL shows it. Markdown becomes elements with md- classes (md-h1 to md-h6, md-p, md-list, md-li, md-strong, md-em, md-code, md-code-span, md-quote, md-table, md-link, md-img and so on). In your own documents, link that stylesheet and override whichever classes you like:

<link type="text/rcss" href="/sagl/ui/theme/base.rcss"/>
<link type="text/rcss" href="/sagl/ui/theme/markdown.rcss"/>
<style> .md-h2 { color: #e1c063; } </style>
...
<sagl-markdown src="docs/help.md"/>