Active Markdown Reference

Notation

The notation for specifying elements is similar to the regular Markdown syntax for links and images, generally following this format:

[text content]{variable_name: configuration}

The text representation of the element, like the link text or image alt text, goes between the brackets, [ ]. The brackets are followed by braces, { }, which contain the variable name, and any configuration. The variable name MUST be followed by a colon, :, if there is any configuration present. The configuration is what determines the kind of element, and specifies its behavior. For example, a RangeElement can be constrained to a minimum and maximum, and a SwitchElement can be given different labels for true and false.

StringElement

[<text>]{<var_name>}

A read-only output of the current value of the specified variable <var_name>. The text is the default value, and used whenever the value of the variable is undefined.

Examples

String, basic

5 - [many eggs]{egg_count}

property value
name egg_count
value 5
egg_count = Math.floor(Math.random() * 10);
String, number formatting

3.14 ish - [3.14 ish]{pi_approx}

property value
name pi_approx
value 3.1428571429
pi_approx = 22/7;
String, undefined

text - [text]{novar}

property value
name novar
value undefined
novar = undefined;

RangeElement

[<before> <number>.<decimal> <after>]{<var_name>: <bound>..<exclusive><bound> by <step>}

A number adjustable through a range of values by dragging or arrow press. The number MAY have a display precision specified. The slider MAY be constrained to a minimum and/or maximum, and MAY have a step value. The text is parsed, and the first number in the text becomes the output value. The remaining text is added to the template, allowing for units and other descriptive text to be included in the control.

A range MUST be specified, but MAY be infinite in both directions. The range’s interval is specified using the CoffeeScript-style range dots, where .. is inclusive and ... excludes the end. ie, 1..4 covers the interval 1 <= n <= 4, while 1...4 covers 1 <= n < 4. The range MUST be ascending (to preserve consistency in the UI — drag left for negative, drag right for positive). The range’s step is specified using the by keyword and a number. The step MAY be omitted (defaulting to 1), but if specified MUST be positive.

The text content MAY include a number to use as the default value. Any surrounding text will be used as a template, allowing units or qualifiers to be included in the element’s presentation.

Specifying a display precision MAY be done using the default number value in the text. 200. formats to 0 decimal places. 200.000 formats to 3 decimals. If not specified, the value is unformatted.

Numbers MAY use the constants in Math and combine them with a coefficient, eg 2pi or 0.5pi, which is treated as n * Math.PI. This can be done in the range min or max, or in the step. The constants MUST be one of e, pi, ln2, ln10, log2e, log10e, sqrt1_2, sqrt2, (uppercase or lowercase).

When the element has focus, the user can press Left/Down and Right/Up arrows to decrement or increment the value by the step. Doing so while holding shift multiplies each press by 10x.

Double-click on a range element to reset to its initial value.

Examples

Specifying step

20. calories - [20. calories]{calories: 10..100 by 10}

property value
name calories
value
interval [10,100]
step 10
default 20
display precision 1
display format "${value.toFixed(0)} calories"
Fractional step, precision

20.0 calories - [20.0 calories]{calories_2: 10..100 by 0.1}

property value
name calories_2
value
interval [10,100]
step 0.1
default 20.0
display precision 0.1
display format "${value.toFixed(1)} calories"
With constants

period 0.00 - [period 0.00]{period: 0..2pi by 0.25pi}

property value
name period
value
interval [0,2pi]
step 0.25pi
default 0
display precision 0.01
display format "${value.toFixed(2)} period"
Unbounded

20 calories - [20 calories]{calories_3: ..}

property value
name calories_3
value
interval (−∞,∞)
step 1
default 20
display precision undefined
display format "${value} calories"
Unbounded right, before text

over 200 calories - [over 200 calories]{calories_4: 190.. by 2}

property value
name calories_4
value
interval [190,∞)
step 2
default 200
display precision undefined
display format "over ${value} calories"

SwitchElement

A boolean flag that has a value of true, false, or undefined. The true and false values can be labeled. If the label is present in the text, that value becomes the default value. Otherwise, the value is undefined.

[<before> <true_label or false_label or *> <after>]{<var_name>: <true_label> or <false_label>}

Once the value is set, it can only be toggled, not unset.

Examples

Switch, basic

pick one - [pick one]{some_flag_1: true or false}

property value
name some_flag_1
value
default undefined
true label true
false label false
display format "#{label}"
Switch, with default

true - [true]{some_flag_3: true or false}

property value
name some_flag_2
value
default true
true label true
false label false
display format "#{label}"
Switch, default, labeled, before/after text

on deck - [on deck]{some_flag_3: on or off}

property value
name some_flag_3
value
default true
true label on
false label off
display format "#{label} deck"

Active Code Blocks

Active Code Blocks are written using regular javascript in indented markdown code blocks. To execute the code, every block is concatenated into a single function body and called given the document variables. The code blocks are editable and changes trigger a refresh of the entire document.


This is some regular markdown text.

    // This is code that will be executed at page load,
    // and whenever a variable changes.

    // This is a local variable that references a document variable.
    // It cannot be referenced by the document or subsequent code
    // block executions.
    const intermediate = 1 + varname;

    // Variables can be assigned a function for use by a ChartEmbed.
    chartFn = function (x) {
      return x * varname;
    }

This is more text in between two code blocks.

    // This is more of the function body.

    // This is a local function usable by the rest
    // of the code body.
    function localFn (arg) {
      return Math.random() * arg > 5;
    }

    // This is assignment to a variable from the document.
    // It is already declared and does not need a declaration
    // keyword (const/let/var).
    if (localFn(intermediate)) {
      docvar = Math.max(100, intermediate);
    } else {
      docvar = Math.max(5, intermediate);
    }

And some final text.

The code MUST be standard javascript supported by the browser. The functions MUST be synchronous. await statements will throw a syntax error. Async functions can be defined and invoked, but changes to variables after the initial execution pass will not be returned to the document. State is not shared between block executions and each execution is an entirely new function definition. Any local variables are not available to the next call.

The blocks all execute within the same scope, in order. Standard hoisting behaviors apply within the code, but note that the variables can be used in active elements before or after the code block in the document.

Libraries

Additional libraries or other scripts can be included for use by the code blocks using inline HTML either as local or external script tags. However, the executor does not currently provide a way to manage script loading and execution order.

DatasetEmbed

Datasets can be embedded into the document, using fenced code blocks. They can be either CSV or JSON, and are available to the code blocks as the specified variable name. JSON is passed through as-is. CSVs are parsed into an Array of Objects keyed using the headers. (Note that CSVs MUST provide a header row.) The datasets are made editable in the rendered page and will trigger a refresh of the code blocks and reactive elements if changed.


```[csv|json]=<varname>
<…data…>
```

Data, JSON

[
  "first",
  "second",
  "third"
]

```json=dataset1
[
  "first",
  "second",
  "third"
]
```

This will be available in the code blocks as:

dataset1 = ['first', 'second', 'third'];

Data, CSV

a,b,c
1,2,3
1,3,5
2,4,8

```csv=dataset2
a,b,c
1,2,3
1,3,5
2,4,8
```

This will be available in the code blocks as:

dataset2 = [
  {
    a: 1,
    b: 2,
    c: 3,
  },
  {
    a: 1,
    b: 3,
    c: 5,
  },
  {
    a: 2,
    b: 4,
    c: 8,
  },
];

ChartEmbed

An embedded chart, of type scatter, line, or bar. The chart is driven by the specified function over the specified range, or the given dataset. The ChartEmbed notation is similar to the RangeElement, but with the addition of the type, and the leading ! (like a markdown image). Also, the chart interval MUST be finite if driven by a function. The variable name MUST be assigned to a function in an ActiveCodeBlock, or match a DatasetEmbed name.

For datasets, and functions that produce objects instead of a number, the range and series labels are used to select the attributes in the record for that point. Multiple series-labels MAY be specified, separated by commas.

![<series_labels,…> <delimiter> <range_label>]{<type>=<varname>: <bound>..<exclusive><bound> by <step>}

Function-driven

Functions used for charts MUST be assigned in a code block to the variable name expected by the chart. They MAY be defined before or after the chart in the document. The functions are called for each item produced by the defined range, and MUST return a result. The result MUST be a number, or an Object with properties that match the range label and series labels of the chart definition.

varname = (x) => {
  // …calculate result…
  return result
}

Data-driven

Datasets used for charts MUST be named with the variable in the chart definition. Either format of Dataset can be used.


```csv=varname
range,series1,series2
1,44,55
2,45,9
3,46,35
```

Examples

Scatter, basic

![y vs x]{scatter=scatterFn: -10..10}

Mult: 0.

scatterFn = function (x) {
  return x + Math.random() * scatter_mult
}

![y vs x]{scatter=scatterFn: -10..10}

property value
name scatterFn
type scatter
x label "x"
y label "y"
interval [-10,10]
step 1
Line, basic

![sin(x)]{line=lineFn: 0..2pi by 0.001}

Frequency 1.00

const b = Math.PI * frequency;
lineFn = function (x) {
  return Math.sin(b * x)
}

sin(x)

property value
name lineFn
type line
x label null
y label null
interval [0,2pi]
step 0.001
Bar, basic

![money by year]{bar=barFn: 1983..2013}

Threshold: 5.

barFn = function (x) {
  return x % threshold;
}

![money by year]{bar=barFn: 1983..2013}

property value
name barFn
type bar
x label "year"
y label "money"
interval [1983,2013]
step 1

Markdown

The activemd command follows GitHub-flavored Markdown which includes some additions to traditional Markdown, such as tables and line breaks in paragraphs. It also supports front-matter metadata.

Front matter

Metadata can be defined in the document using YAML-based front matter. Only the title is used by Active Markdown for now, as the document title. The entirety of the metadata is available to code blocks as meta.

---
title: Document title
somedate: 2024-09-01
---

Headers 1–6 are given ids with slugified versions of their content, and the rendered form will produce an <a> tag next to each header for easy deep linking.

Example

This content below is just for a complete demonstration of the styling:

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
enim ad minim veniam, quis nostrud exercitation ullamco laboris
nisi ut aliquip ex ea commodo consequat.

  1. first
  2. second
    a. Aleph
    b. Bet
  3. third
column 1 column 2 column 3
row1 a row1 b row1 c
row2 a row2 b row2 c
row3 a row3 b row3 c
row4 a row4 b row4 c

Blue Marble photo from Apollo 17 of the Earth from space;

activemd Command

The activemd command is used to compile Active Markdown content into an interactive HTML file to be viewed in a browser. It will render the regular markdown content, create the active elements, and inject scripts and styles needed for interactivity. By default everything is inlined into the page so no external library links are needed. It also includes the original markdown source, viewable from the rendered page. This inlining ensures maximum portability and durability of the rendered version.

Compile

activemd compile <filename.md> or activemd <filename.md> will compile the given file into <filename>.html. Note that it will overwrite any existing content at the output filename without warning.

Optionally specify a title to use in the document head with --title="Page title". This will override any front matter-specified title.

Watch

activemd watch <filename.md> will compile the given file, then watch for changes and recompile. It also injects a simple live reload script that will reload the browser after recompilation, for quick feedback while editing. It does not provide a server so you have to open the compiled file directly.

Use CTRL+C to quit the watcher.

Sample

activemd sample will generate a sample.md file in the current working directory.


Advanced

The Active Code Blocks automatically get access to the variables when executed. But if poking around in the console, you can access the document variables using the window.ActiveMarkdown object. Each variable has a getValue() method to retrieve the current value. The elements and embeds are also indexed there.

Note: this is not part of the public API and could change at any time. It’s provided for the sake of experimentation, learning, and debugging.