@@ -4,6 +4,7 @@ import {type Patch, type PatchItem, getPatch} from "fast-array-diff";
44import equal from "fast-deep-equal" ;
55import matter from "gray-matter" ;
66import hljs from "highlight.js" ;
7+ import katex from "katex" ;
78import { parseHTML } from "linkedom" ;
89import MarkdownIt from "markdown-it" ;
910import { type RuleCore } from "markdown-it/lib/parser_core.js" ;
@@ -79,18 +80,32 @@ function uniqueCodeId(context: ParseContext, content: string): string {
7980 return id ;
8081}
8182
82- function getLiveSource ( content , language , option ) {
83+ function getLiveSource ( content , language , option ) : { source ?: string ; html ?: string } {
8384 return option === "no-run"
84- ? undefined
85+ ? { }
8586 : language === "js"
86- ? content
87+ ? { source : content }
8788 : language === "tex"
88- ? transpileTag ( content , "tex.block" , true )
89+ ? maybeStaticTeX ( content , true )
8990 : language === "dot"
90- ? transpileTag ( content , "dot" , false )
91+ ? { source : transpileTag ( content , "dot" , false ) }
9192 : language === "mermaid"
92- ? transpileTag ( content , "await mermaid" , false )
93- : undefined ;
93+ ? { source : transpileTag ( content , "await mermaid" , false ) }
94+ : { } ;
95+ }
96+
97+ function maybeStaticTeX ( content , displayMode ) {
98+ try {
99+ // TODO smarter detection of ${} contents
100+ // TODO smarter insertion of the TeX stylesheet
101+ return {
102+ html :
103+ katex . renderToString ( content , { displayMode} ) +
104+ `<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/katex/dist/katex.min.css">`
105+ } ;
106+ } catch {
107+ return { source : transpileTag ( content , displayMode ? "tex.block" : "tex" , true ) } ;
108+ }
94109}
95110
96111function makeFenceRenderer ( root : string , baseRenderer : RenderRule , sourcePath : string ) : RenderRule {
@@ -99,7 +114,7 @@ function makeFenceRenderer(root: string, baseRenderer: RenderRule, sourcePath: s
99114 const [ language , option ] = token . info . split ( " " ) ;
100115 let result = "" ;
101116 let count = 0 ;
102- const source = getLiveSource ( token . content , language , option ) ;
117+ const { source, html } = getLiveSource ( token . content , language , option ) ;
103118 if ( source != null ) {
104119 const id = uniqueCodeId ( context , token . content ) ;
105120 const sourceLine = context . startLine + context . currentLine ;
@@ -115,6 +130,7 @@ function makeFenceRenderer(root: string, baseRenderer: RenderRule, sourcePath: s
115130 result += `<div id="cell-${ id } " class="observablehq observablehq--block"></div>\n` ;
116131 count ++ ;
117132 }
133+ if ( html !== undefined ) result += html ;
118134 if ( source == null || option === "show" ) {
119135 result += baseRenderer ( tokens , idx , options , context , self ) ;
120136 count ++ ;
@@ -258,6 +274,13 @@ function makePlaceholderRenderer(root: string, sourcePath: string): RenderRule {
258274 return ( tokens , idx , options , context : ParseContext ) => {
259275 const id = uniqueCodeId ( context , tokens [ idx ] . content ) ;
260276 const token = tokens [ idx ] ;
277+
278+ // inline TeX?
279+ if ( token . content . match ( / ^ t e x [ ` ] / ) ) {
280+ const { html} = maybeStaticTeX ( token . content . slice ( 4 , - 1 ) , false ) ;
281+ if ( html !== undefined ) return `<span id="cell-${ id } ">${ html } </span>` ;
282+ }
283+
261284 const transpile = transpileJavaScript ( token . content , {
262285 id,
263286 root,
0 commit comments