Scriptorium User's Guide

This user's guide is a work-in-progress. Please keep checking back for more information. If you have any questions or feedback, don't hesitate to ask me on the fediverse or on the Gleam Discord (user Nicd).

Table of Contents

  1. Prerequisites & Installation
  2. Default Filesystem Layout
  3. Writing a Post

Prerequisites & Installation

Scriptorium requires the following software:

  • Gleam 1.1+
  • Node.js (tested on version 20)

In addition, you will need at least a preliminary understanding of the Gleam language. The default blog setup does not require any special knowledge, but customizing the generation process will require writing Gleam code.

Typically creating a blog with Scriptorium requires creating a new Gleam project. When you have a Gleam project, you can add the library as a dependency by using gleam add scriptorium.

To set up a basic blog, replace the project's main file with the following:

import gleam/result
import gleam/option
import gleam/io
import scriptorium/builder
import scriptorium/config.{type Configuration}
import scriptorium/defaults

pub fn main() {
  let config =
    defaults.default_config(
      "My Blog",
      "https://my.blog.example/",
      "en",
      config.Author(
        "Person McPerson",
        option.Some("person@example.com"),
        option.Some("https://fedi.instance.example/@person"),
      ),
      "© Person McPerson",
    )

  io.debug(build(config))
}

pub fn build(config: Configuration) {
  // Parse the files
  use db <- result.try(builder.parse(config))
  // Compile Markdown into HTML
  let compiled = builder.compile(db, config)
  // Render content into Lustre elements
  let rendered = builder.render(db, compiled, config)
  // Write rendered content into the filesystem
  builder.write(rendered, config)
}

This is the minimum setup for building a blog. Now the blog can be generated with gleam run.

Default Filesystem Layout

By default the blog generator expects the following folders to exist:

  • data – Master folder for input data, inside the root of the project folder.
    • posts – Folder for posts.
    • pages – Folder for pages.

As an illustrative example of the contents, the filesystem contents of this blog at the time of writing were:

data
├── menu
├── pages
│   ├── 404.md
│   └── guide.md
└── posts
    ├── 2024-04-14-hello-world.md
    └── 2024-04-21-scriptorium-published.md

Writing a Post

Post Filename

To write a post, create a new file in the ./data/posts folder. The filename must consist of the following:

  • an ISO 8601 formatted date, i.e. YYYY-MM-DD,
  • a dash,
  • an optional zero-padded 2-digit order number used for ordering when two posts were written on the same day and don't have time information, followed by a dash,
  • a slug that is a free-form name for the post used in the post filename and thus the final URL, and
  • the file extension .md.

Note that because the slug is used in the filename and in the URL, it should only contain filename and URL safe text. The recommendation is to stick to regular characters, dashes, and underscores, without any spaces. "Regular characters" here does not exclude non-latin characters, as they are safely percent-encoded in a URL.

Post Contents

Scriptorium posts are written in Markdown. Marked.js is used for compiling Markdown, so its documentation should be consulted when there is a question about how something is rendered.

Header

There are, however, some special parts at the start of a post. Let's look at an example:

Post Title
tag1, tag2, tag3
time: 21:15 Europe/Helsinki
description: Example post
image: https://example.com/example.jpg

Hello, and welcome to this example post!

The first line in a file is the post title. After that, optionally, come post tags on their own line, separated by a comma. Post tags are used as-is, so they also should contain only filename and URL safe content, and no spaces.

After the tags are headers. Headers are a collection of key-value pairs, separated by a colon. They can contain various metadata about the post, and the user can implement their own headers by customizing the post view. There are some predefined headers:

  • time – Defines the time when the post was written. The format is hh:mm TZ, where the first part is the local time using a 24 hour clock, and the second is the local timezone identifier.
  • description – A description of the post that is used for link embeds on external services.
  • image – An image that is used for link embeds on external services. Should be on the smaller side and must be an absolute URL.
Splitting

Often it is not sensible to show the whole post in list views with many posts. In this case the post can be divided into two pieces. In the list view, only the first part is shown, and in the individual post page, both parts are shown.

To do this, insert <!-- SPLIT --> in the post. It's recommended to put this on its own line and not inside any HTML or Markdown content that would get broken when split apart.