Skip to content

Commit

Permalink
first stab at inline SSR TeX
Browse files Browse the repository at this point in the history
  • Loading branch information
Fil committed Nov 11, 2023
1 parent 2078c98 commit 9532757
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 9 deletions.
10 changes: 10 additions & 0 deletions docs/tex.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,13 @@ c = \pm\sqrt{a^2 + b^2}
\f\hat\xi\,e^{2 \pi i \xi x}
\,d\xi
```

When possible, a ${tex`\TeX`} expression is compiled server-side, for faster rendering; however, when it uses variables, it needs to load the katex library, and to be compiled client-side:

```js
const a = view(Inputs.range([0, 5], {step: 1}));
```

```tex show
\int_0^1 x^${a}dx = \frac1${a + 1}
```
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"fast-deep-equal": "^3.1.3",
"gray-matter": "^4.0.3",
"highlight.js": "^11.8.0",
"katex": "^0.16.9",
"linkedom": "^0.15.6",
"markdown-it": "^13.0.2",
"markdown-it-anchor": "^8.6.7",
Expand Down
1 change: 1 addition & 0 deletions public/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ async function mermaid() {

export function define(cell) {
const {id, inline, inputs = [], outputs = [], files = [], databases = [], body} = cell;
if (body === undefined) return;
const variables = [];
cellsById.get(id)?.variables.forEach((v) => v.delete());
cellsById.set(id, {cell, variables});
Expand Down
41 changes: 32 additions & 9 deletions src/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {type Patch, type PatchItem, getPatch} from "fast-array-diff";
import equal from "fast-deep-equal";
import matter from "gray-matter";
import hljs from "highlight.js";
import katex from "katex";
import {parseHTML} from "linkedom";
import MarkdownIt from "markdown-it";
import {type RuleCore} from "markdown-it/lib/parser_core.js";
Expand Down Expand Up @@ -79,18 +80,32 @@ function uniqueCodeId(context: ParseContext, content: string): string {
return id;
}

function getLiveSource(content, language, option) {
function getLiveSource(content, language, option): {source?: string; html?: string} {
return option === "no-run"
? undefined
? {}
: language === "js"
? content
? {source: content}
: language === "tex"
? transpileTag(content, "tex.block", true)
? maybeStaticTeX(content, true)
: language === "dot"
? transpileTag(content, "dot", false)
? {source: transpileTag(content, "dot", false)}
: language === "mermaid"
? transpileTag(content, "await mermaid", false)
: undefined;
? {source: transpileTag(content, "await mermaid", false)}
: {};
}

function maybeStaticTeX(content, displayMode) {
try {
// TODO smarter detection of ${} contents
// TODO smarter insertion of the TeX stylesheet
return {
html:
katex.renderToString(content, {displayMode}) +
`<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/katex/dist/katex.min.css">`
};
} catch {
return {source: transpileTag(content, displayMode ? "tex.block" : "tex", true)};
}
}

function makeFenceRenderer(root: string, baseRenderer: RenderRule, sourcePath: string): RenderRule {
Expand All @@ -99,7 +114,7 @@ function makeFenceRenderer(root: string, baseRenderer: RenderRule, sourcePath: s
const [language, option] = token.info.split(" ");
let result = "";
let count = 0;
const source = getLiveSource(token.content, language, option);
const {source, html} = getLiveSource(token.content, language, option);
if (source != null) {
const id = uniqueCodeId(context, token.content);
const sourceLine = context.startLine + context.currentLine;
Expand All @@ -109,12 +124,13 @@ function makeFenceRenderer(root: string, baseRenderer: RenderRule, sourcePath: s
sourcePath,
sourceLine
});
extendPiece(context, {code: [transpile]});
if (transpile.files) context.files.push(...transpile.files);
if (transpile.imports) context.imports.push(...transpile.imports);
extendPiece(context, {code: [transpile]});
result += `<div id="cell-${id}" class="observablehq observablehq--block"></div>\n`;
count++;
}
if (html !== undefined) result += html;
if (source == null || option === "show") {
result += baseRenderer(tokens, idx, options, context, self);
count++;
Expand Down Expand Up @@ -258,6 +274,13 @@ function makePlaceholderRenderer(root: string, sourcePath: string): RenderRule {
return (tokens, idx, options, context: ParseContext) => {
const id = uniqueCodeId(context, tokens[idx].content);
const token = tokens[idx];

// inline TeX?
if (token.content.match(/^tex[`]/)) {
const {html} = maybeStaticTeX(token.content.slice(4, -1), false);
if (html !== undefined) return `<span id="cell-${id}">${html}</span>`;
}

const transpile = transpileJavaScript(token.content, {
id,
root,
Expand Down
12 changes: 12 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -654,6 +654,11 @@ commander@7:
resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==

commander@^8.3.0:
version "8.3.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66"
integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==

[email protected]:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
Expand Down Expand Up @@ -1794,6 +1799,13 @@ json5@^1.0.2:
dependencies:
minimist "^1.2.0"

katex@^0.16.9:
version "0.16.9"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.9.tgz#bc62d8f7abfea6e181250f85a56e4ef292dcb1fa"
integrity sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==
dependencies:
commander "^8.3.0"

keyv@^4.5.3:
version "4.5.4"
resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93"
Expand Down

0 comments on commit 9532757

Please sign in to comment.