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)
}
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
---
Deep links
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:
- foo
- bar
- baz
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.
- first
- second
a. Aleph
b. Bet - 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 |
;
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.