-
Notifications
You must be signed in to change notification settings - Fork 243
feat: Add support for TikZJax diagrams. #337
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,7 @@ const tocPlugin = require("eleventy-plugin-nesting-toc"); | |
| const { parse } = require("node-html-parser"); | ||
| const htmlMinifier = require("html-minifier-terser"); | ||
| const pluginRss = require("@11ty/eleventy-plugin-rss"); | ||
| const tex2svg = require("node-tikzjax").default; | ||
|
|
||
| const { headerToId, namedHeadingsFilter } = require("./src/helpers/utils"); | ||
| const { | ||
|
|
@@ -202,6 +203,11 @@ module.exports = function (eleventyConfig) { | |
| )}</div></div>`; | ||
| return res | ||
| } | ||
| if (token.info === "tikz") { | ||
| const code = token.content.trim(); | ||
| const b64 = Buffer.from(code, "utf8").toString("base64"); // Encode into Base64 to avoid issues with special characters in TeX code | ||
| return `<div class="block-language-tikz">${b64}</div>`; | ||
| } | ||
|
|
||
| // Other languages | ||
| return origFenceRule(tokens, idx, options, env, slf); | ||
|
|
@@ -526,6 +532,65 @@ module.exports = function (eleventyConfig) { | |
| return content; | ||
| }); | ||
|
|
||
| eleventyConfig.addTransform("render-tikzjax", (() => { | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is registered after htmlMinifier, so in production the minifier runs first. Consider moving it before htmlMinifier to avoid fragile ordering.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| let tikzQueue = Promise.resolve(); | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a comment explaining why rendering is serialized (TeX engine likely isn't concurrency-safe), since the promise chain pattern is non-obvious.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| // https://github.com/artisticat1/obsidian-tikzjax/blob/main/main.ts | ||
| function tidyTikzSource(src) { | ||
| if (!src) return src; | ||
| return src | ||
| .replaceAll(" ", "") // Remove non-breaking space characters, otherwise we get errors | ||
| .replace(/\u00A0/g, "") | ||
| .replace(/\r\n/g, "\n") // Normalize line endings & Split into lines | ||
| .split("\n") | ||
| .map((line) => line.trim()) // Trim whitespace that is inserted when pasting in code, otherwise TikZJax complains | ||
| .filter((line) => line.length > 0) // Remove empty lines | ||
| .join("\n"); | ||
| } | ||
|
|
||
| return async function (content, outputPath) { | ||
| if (!outputPath || !outputPath.endsWith(".html")) return content; | ||
| if (!content || !content.includes('class="block-language-tikz"')) return content; | ||
|
|
||
| const root = parse(content); | ||
| const blocks = root.querySelectorAll("div.block-language-tikz"); | ||
| if (!blocks.length) return content; | ||
|
|
||
| for (const block of blocks) { | ||
| const srcB64 = block.text || ""; | ||
| const texSource = tidyTikzSource(Buffer.from(srcB64, "base64").toString("utf8")); | ||
| try { | ||
| const run = tikzQueue | ||
| .catch(() => {}) | ||
| .then(() => | ||
| tex2svg(texSource, { | ||
| // SvgOptions | ||
| embedFontCss: true, // Whether to embed the font CSS file in the SVG. | ||
| // TeXOptions | ||
| showConsole: false, // Print log of TeX engine to console. | ||
| texPackages: {}, // Additional TeX packages to load. e.g. texPackages: { pgfplots: '', amsmath: 'intlimits' }, | ||
| tikzLibraries: '', // Additional TikZ libraries to load. e.g. tikzLibraries: 'arrows.meta,calc' | ||
| }) | ||
| ); | ||
| tikzQueue = run.catch(() => {}); | ||
| const svg = await run; | ||
|
|
||
| const svgElement = parse(svg) | ||
| .querySelector("svg") // after zooming some pictures could go wrong, | ||
|
||
| .setAttribute("style", "margin:auto; display:block;"); // e.g. oleeskild/obsidian-digital-garden#667 | ||
| block.replaceWith(svgElement); | ||
| } catch (e) { | ||
| console.warn("\n[TikZJax] render failed at:", outputPath); | ||
| console.warn("[TikZJax] TeX source (first 400 chars):\n", texSource.slice(0, 400)); | ||
| console.warn("[TikZJax] Warn:", e); | ||
| block.replaceWith(`<pre><code class="language-tikz">TikZ render failed. See build log.\n\n${(texSource)}</code></pre>`); | ||
|
||
| } | ||
| } | ||
|
|
||
| return root.toString(); | ||
| }; | ||
| })()); | ||
|
|
||
| eleventyConfig.addPassthroughCopy("src/site/img"); | ||
| eleventyConfig.addPassthroughCopy("src/site/scripts"); | ||
| eleventyConfig.addPassthroughCopy("src/site/styles/_theme.*.css"); | ||
|
|
||
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.





There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider lazy-loading this inside the transform on first use, so the TeX engine isn't loaded on builds with no TikZ blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LukeBriton@3a3176e#diff-c306e0a99961a16f5c5c83996caa0958b94006d97f97475049ea3a08036bb5b0R511
