@@ -2,6 +2,9 @@ import React, { useState, useEffect, useRef, useCallback } from 'react';
22import LZString from 'lz-string' ;
33import { parse } from 'toml' ;
44import configRaw from './excalidraw.config.toml?raw' ;
5+ import katex from 'katex' ;
6+ import 'katex/dist/katex.min.css' ;
7+ import katexStyles from 'katex/dist/katex.min.css?raw' ;
58
69const config = parse ( configRaw ) ;
710
@@ -90,6 +93,19 @@ export function Excalidraw({
9093
9194 const textContent = await res . text ( ) ;
9295 let json ;
96+ // Match lines like "hash: $$formula$$" for LaTeX extraction
97+ const latexMap : Record < string , string > = { } ;
98+ const latexLines = textContent . match ( / ^ [ a - f 0 - 9 ] { 40 } : \$ \$ .* ?\$ \$ / gm) ;
99+ if ( latexLines ) {
100+ latexLines . forEach ( line => {
101+ const colonIndex = line . indexOf ( ':' ) ;
102+ if ( colonIndex > 0 ) {
103+ const id = line . slice ( 0 , colonIndex ) . trim ( ) ;
104+ const formula = line . slice ( colonIndex + 1 ) . trim ( ) . replace ( / ^ \$ \$ / , '' ) . replace ( / \$ \$ $ / , '' ) ;
105+ latexMap [ id ] = formula ;
106+ }
107+ } ) ;
108+ }
93109
94110 // Try parsing as standard JSON first
95111 try {
@@ -104,6 +120,43 @@ export function Excalidraw({
104120 const decompressed = LZString . decompressFromBase64 ( compressed ) ;
105121 if ( decompressed ) {
106122 json = JSON . parse ( decompressed ) ;
123+
124+ // Populate json.files with LaTeX renders if we found any
125+ if ( Object . keys ( latexMap ) . length > 0 ) {
126+ json . files = json . files || { } ;
127+ for ( const [ id , formula ] of Object . entries ( latexMap ) ) {
128+ // Find corresponding image element to get its intended size
129+ const el = json . elements ?. find ( ( e : any ) => e . fileId === id && ! e . isDeleted ) ;
130+ if ( ! el ) continue ;
131+
132+ try {
133+ const html = katex . renderToString ( formula , { displayMode : true , throwOnError : false } ) ;
134+ // Create a self-contained SVG with inlined KaTeX CSS
135+ const svgString = `
136+ <svg xmlns="http://www.w3.org/2000/svg" width="${ el . width } " height="${ el . height } ">
137+ <foreignObject width="100%" height="100%">
138+ <div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; overflow: hidden;">
139+ <style>
140+ ${ katexStyles }
141+ .katex-display { margin: 0; }
142+ .katex { font-size: 1.1em; }
143+ </style>
144+ ${ html }
145+ </div>
146+ </foreignObject>
147+ </svg>` . trim ( ) ;
148+ const dataURL = `data:image/svg+xml;base64,${ btoa ( unescape ( encodeURIComponent ( svgString ) ) ) } ` ;
149+ json . files [ id ] = {
150+ mimeType : "image/svg+xml" ,
151+ id,
152+ dataURL,
153+ created : Date . now ( )
154+ } ;
155+ } catch ( err ) {
156+ console . error ( "KaTeX rendering failed for ID:" , id , err ) ;
157+ }
158+ }
159+ }
107160 } else {
108161 throw new Error ( "Failed to decompress Excalidraw data" ) ;
109162 }
0 commit comments