Published
Edited
Oct 14, 2021
Importers
1 star
Insert cell
Insert cell
lmd`Inline $\KaTeX$ $\mathit{Hello\ world}$

> $$
> \begin{aligned}
> \frac{\mathrm{d}🐁}{\mathrm{d}t} &= \alpha 🐁-\beta 🐁🐈 \\[2ex]
> \frac{\mathrm{d}🐈}{\mathrm{d}t} &= \delta 🐁🐈-\gamma 🐈
> \end{aligned}
> $$
> — @mbostock, [Emoji in $\LaTeX$](https://observablehq.com/@mbostock/emoji-in-latex)`
Insert cell
Insert cell
md`${tex`\textbf{\textit{v}} = \left[\begin{matrix}v_1\\ v_2\\\end{matrix}\right]`} and
${tex`\textbf{\textit{w}} = \left[\begin{matrix}w_{1}\\ w_{2}\end{matrix}\right]`} add to
${tex`\textbf{\textit{v}} + \textbf{\textit{w}} = \textbf{\textit{w}} = \left[\begin{matrix}v_{1} + w_{1}\\ v_{2} + w_{2}\end{matrix}\right]`}.`
Insert cell
Insert cell
lmd`$\textbf{\textit{v}} = \left[\begin{matrix}v_1\\ v_2\\\end{matrix}\right]$
and $\textbf{\textit{w}} = \left[\begin{matrix}w_{1}\\ w_{2}\end{matrix}\right]$
add to $\textbf{\textit{v}} + \textbf{\textit{w}} = \left[\begin{matrix}v_{1} + w_{1}\\ v_{2} + w_{2}\end{matrix}\right]$.`
Insert cell
Insert cell
Insert cell
extensions = {
// https://github.com/markedjs/marked/blob/1d886aa0de3ed59ad883b820863135b4a45c09c5/src/rules.js#L163
const inlineRule = /^(\$+)([^\$]|[^\$][\s\S]*?[^\$])\1(?!\$)/; // same as codespan but with $ instead of `

// https://github.com/markedjs/marked/blob/1d886aa0de3ed59ad883b820863135b4a45c09c5/src/rules.js#L13
const blockRule = /^(\${2})\n(?:|([\s\S]*?)\n)(?: {0,3}\1[\$]* *(?=\n|$)|$)/; // same as fences but with $$ and adapted

//https://marked.js.org/using_pro#extensions
return [
{
name: "latexcode",
level: "inline",
start(src) {
return src.match(/\$/)?.index;
},
tokenizer(src, tokens) {
// https://github.com/markedjs/marked/blob/1d886aa0de3ed59ad883b820863135b4a45c09c5/src/Tokenizer.js#L634
const cap = inlineRule.exec(src);
if (cap) {
let text = cap[2].replace(/\n/g, " ");
const hasNonSpaceChars = /[^ ]/.test(text);
const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
text = text.substring(1, text.length - 1);
}
// text = escape(text, true);
return { type: "latexcode", raw: cap[0], text };
}
},
renderer(token) {
return tex`${token.text}`.outerHTML;
}
},
{
name: "latexblock",
level: "block",
start(src) {
return src.match(/\${2}/)?.index;
},
tokenizer(src, tokens) {
// https://github.com/markedjs/marked/blob/1d886aa0de3ed59ad883b820863135b4a45c09c5/src/Tokenizer.js#L101
const cap = blockRule.exec(src);
if (cap) {
const raw = cap[0];
const text = indentLatexCompensation(raw, cap[2] || "");

return { type: "latexblock", raw, text };
}
},
renderer(token) {
return tex.block`${token.text}`.outerHTML;
}
}
];
}
Insert cell
function indentLatexCompensation(raw, text) {
// https://github.com/markedjs/marked/blob/1d886aa0de3ed59ad883b820863135b4a45c09c5/src/Tokenizer.js#L37
const matchIndentToCode = raw.match(/^(\s+)(?:\$\$)/);

if (matchIndentToCode === null) {
return text;
}

const indentToCode = matchIndentToCode[1];

return text
.split("\n")
.map((node) => {
const matchIndentInNode = node.match(/^\s+/);
if (matchIndentInNode === null) {
return node;
}

const [indentInNode] = matchIndentInNode;

if (indentInNode.length >= indentToCode.length) {
return node.slice(indentToCode.length);
}

return node;
})
.join("\n");
}
Insert cell
Insert cell
rawify = (callback) =>
function () {
arguments[0] = arguments[0].map((_v, i, strs) => strs.raw[i]); // pass raw strings instead of classic ones to support latex commands
return callback.apply(null, arguments);
}
Insert cell
tmd = ({ block: lmd })
Insert cell
Insert cell
lmd = await require("marked@3").then(function (marked) {
// https://github.com/observablehq/stdlib/blob/b9bbd5c71147a15a75fdfa5cdb85d9d009f2625b/src/md.js#L4
// but with marked@3 instead of marked@0.3.x
marked.use({ extensions });

return rawify(
template(
function () {
var root = document.createElement("div");
root.innerHTML = marked(arguments[0], { langPrefix: "" }).trim();
var code = root.querySelectorAll("pre code[class]");
if (code.length > 0) {
require("@observablehq/highlight.js@2").then(function (hl) {
code.forEach(function (block) {
function done() {
hl.highlightBlock(block);
block.parentNode.classList.add("observablehq--md-pre");
}
if (hl.getLanguage(block.className)) {
done();
} else {
require("@observablehq/highlight.js@2/async-languages/index.js")
.then((index) => {
if (index.has(block.className)) {
return require("@observablehq/highlight.js@2/async-languages/" +
index.get(block.className)).then((language) => {
hl.registerLanguage(block.className, language);
});
}
})
.then(done, done);
}
});
});
}
return root;
},
function () {
return document.createElement("div");
}
)
);
})
Insert cell
template = (await import("@observablehq/stdlib@3.14.8/src/template.js")).default
Insert cell
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more