Files
uni_notes/.obsidian/plugins/obsidian-typst-math/main.js
2026-02-27 11:13:09 +01:00

806 lines
30 KiB
JavaScript

/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// main.ts
var main_exports = {};
__export(main_exports, {
default: () => TypstMathPlugin
});
module.exports = __toCommonJS(main_exports);
var import_child_process = require("child_process");
var import_fs = require("fs");
var import_obsidian = require("obsidian");
var os = __toESM(require("os"));
var path = __toESM(require("path"));
var DEFAULT_SETTINGS = {
typstPath: "typst",
enableInlineMath: true,
enableDisplayMath: true,
debugMode: false,
showDetailedErrors: false,
useWatchMode: false,
enableLivePreview: true,
forceSyncCompile: false,
syncOnlyDuringExport: true
};
var TypstMathPlugin = class extends import_obsidian.Plugin {
constructor() {
super(...arguments);
// Internal flag set when a print/export flow is active
this.exportInProgress = false;
// Hold original window.print so we can restore it on unload
this._originalWindowPrint = null;
// Keep reference to matchMedia listener so we can remove it
this._printMediaListener = null;
this.watchers = /* @__PURE__ */ new Map();
// Store typst source content for blocks that are pending compilation
this.pendingTypstContent = /* @__PURE__ */ new Map();
this.renderCache = /* @__PURE__ */ new Map();
this.handleBeforePrint = () => {
this.exportInProgress = true;
if (this.settings.debugMode)
console.log("Typst plugin: export/print started");
};
this.handleAfterPrint = () => {
this.exportInProgress = false;
if (this.settings.debugMode)
console.log("Typst plugin: export/print finished");
};
}
async onload() {
var _a;
await this.loadSettings();
this.tempDir = path.join(os.tmpdir(), "obsidian-typst-math");
await this.ensureTempDir();
this.registerMarkdownCodeBlockProcessor(
"math",
this.processMathBlock.bind(this)
);
this.registerMarkdownCodeBlockProcessor(
"typst",
this.processMathBlock.bind(this)
);
await (0, import_obsidian.loadMathJax)();
if (!globalThis.MathJax) {
new import_obsidian.Notice("MathJax failed to load. Math rendering may not work.");
console.error("MathJax failed to load.");
} else {
this.originalTex2chtml = globalThis.MathJax.tex2chtml;
globalThis.MathJax.tex2chtml = (latex, options) => {
return this.renderWithTypst(latex, options);
};
const activeView = this.app.workspace.getActiveViewOfType(import_obsidian.MarkdownView);
if (activeView) {
(_a = activeView.previewMode) == null ? void 0 : _a.rerender(true);
if (this.settings.enableLivePreview) {
const editor = activeView.editor;
if (editor) {
const cursor = editor.getCursor();
editor.setCursor(cursor);
}
}
}
}
if (typeof window !== "undefined") {
window.addEventListener("beforeprint", this.handleBeforePrint);
window.addEventListener("afterprint", this.handleAfterPrint);
try {
if (typeof window.print === "function") {
this._originalWindowPrint = window.print.bind(window);
window.print = (...args) => {
this.handleBeforePrint();
try {
const res = this._originalWindowPrint(...args);
setTimeout(() => this.handleAfterPrint(), 1500);
return res;
} catch (e) {
this.handleAfterPrint();
throw e;
}
};
}
} catch (e) {
if (this.settings.debugMode)
console.warn("Failed to patch window.print", e);
}
try {
const mql = window.matchMedia && window.matchMedia("print");
if (mql) {
this._printMediaListener = (ev) => {
if (ev.matches)
this.handleBeforePrint();
else
this.handleAfterPrint();
};
if (typeof mql.addEventListener === "function") {
mql.addEventListener("change", this._printMediaListener);
} else if (typeof mql.addListener === "function") {
mql.addListener(this._printMediaListener);
}
}
} catch (e) {
if (this.settings.debugMode)
console.warn("Failed to attach matchMedia print listener", e);
}
}
this.addSettingTab(new TypstMathSettingTab(this.app, this));
this.addCommand({
id: "typst-prepare-export",
name: "Typst: Prepare export (sync compile all math)",
callback: () => {
void this.prepareExport();
}
});
console.log("Typst Math Plugin loaded");
}
async onunload() {
var _a;
if (this.originalTex2chtml && globalThis.MathJax) {
globalThis.MathJax.tex2chtml = this.originalTex2chtml;
(_a = this.app.workspace.getActiveViewOfType(import_obsidian.MarkdownView)) == null ? void 0 : _a.previewMode.rerender(true);
}
for (const [id, process] of this.watchers) {
process.kill();
}
this.watchers.clear();
try {
await import_fs.promises.rm(this.tempDir, { recursive: true, force: true });
} catch (error) {
console.error("Error cleaning up temp directory:", error);
}
try {
if (typeof window !== "undefined") {
window.removeEventListener("beforeprint", this.handleBeforePrint);
window.removeEventListener("afterprint", this.handleAfterPrint);
if (this._printMediaListener && window.matchMedia) {
const mql = window.matchMedia("print");
if (mql) {
if (typeof mql.removeEventListener === "function") {
mql.removeEventListener("change", this._printMediaListener);
} else if (typeof mql.removeListener === "function") {
mql.removeListener(this._printMediaListener);
}
}
}
if (this._originalWindowPrint) {
try {
window.print = this._originalWindowPrint;
} catch (e) {
}
}
}
} catch (e) {
if (this.settings.debugMode)
console.warn("Error restoring print handlers", e);
}
console.log("Typst Math Plugin unloaded");
}
async loadSettings() {
this.settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData());
}
async saveSettings() {
await this.saveData(this.settings);
}
async ensureTempDir() {
try {
await import_fs.promises.mkdir(this.tempDir, { recursive: true });
} catch (error) {
console.error("Error creating temp directory:", error);
}
}
formatError(error, isInline = false) {
const errorMsg = typeof error === "string" ? error : error.message;
let friendlyMsg = "There was an error with your Typst code";
if (errorMsg.includes("ENOENT") || errorMsg.includes("not found")) {
friendlyMsg = "Typst CLI not found";
} else if (errorMsg.includes("syntax error") || errorMsg.includes("unexpected")) {
friendlyMsg = "Syntax error in Typst code";
} else if (errorMsg.includes("undefined")) {
friendlyMsg = "Undefined symbol or function";
} else if (errorMsg.includes("type")) {
friendlyMsg = "Type error in expression";
}
const errorClass = isInline ? "typst-error-inline" : "typst-error";
if (this.settings.showDetailedErrors) {
return `<span class="${errorClass}">${friendlyMsg}</span><span class="typst-error-details">${errorMsg}</span>`;
} else {
return `<span class="${errorClass}">${friendlyMsg}</span>`;
}
}
async processMathBlock(source, el, ctx) {
const blockId = this.generateBlockId(source, ctx.sourcePath);
if (this.renderCache.has(blockId)) {
el.empty();
const container2 = el.createDiv({ cls: "typst-math-container" });
const cached = this.renderCache.get(blockId);
container2.innerHTML = cached != null ? cached : "";
return;
}
el.empty();
const container = el.createDiv({ cls: "typst-math-container" });
container.innerHTML = '<div class="typst-loading">Rendering with Typst...</div>';
container.setAttribute("data-typst-blockid", blockId);
try {
const typstContent = this.wrapInTypstDocument(source);
const typstFile = path.join(this.tempDir, `${blockId}.typ`);
const htmlFile = path.join(this.tempDir, `${blockId}.html`);
this.pendingTypstContent.set(blockId, typstContent);
await import_fs.promises.writeFile(typstFile, typstContent, "utf-8");
if (this.settings.useWatchMode) {
await this.renderTypstWithWatch(typstFile, htmlFile, container, blockId);
} else {
await this.renderTypstToHtml(typstFile, htmlFile, container, blockId);
}
} catch (error) {
container.innerHTML = this.formatError(error, false);
if (this.settings.debugMode) {
console.error("Typst rendering error:", error);
}
}
}
renderWithTypst(latex, options) {
const isBlock = options.display || false;
if (isBlock && !this.settings.enableDisplayMath) {
return this.originalTex2chtml(latex, options);
}
if (!isBlock && !this.settings.enableInlineMath) {
return this.originalTex2chtml(latex, options);
}
if (this.hasLatexCommand(latex)) {
}
const typstContent = this.convertLatexToTypst(latex);
const blockId = this.generateBlockId(typstContent, `math-${Date.now()}`);
const container = document.createElement(isBlock ? "div" : "span");
container.className = isBlock ? "typst-math-container" : "typst-math-inline";
container.innerHTML = '<span class="typst-loading">Rendering with Typst...</span>';
try {
container.setAttribute("data-typst-blockid", blockId);
this.pendingTypstContent.set(blockId, typstContent);
} catch (e) {
}
this.renderTypstMath(typstContent, container, blockId, isBlock);
return container;
}
hasLatexCommand(expr) {
return false;
}
convertLatexToTypst(latex) {
let content = latex.trim();
content = content.replace(/^\$\$/, "").replace(/\$\$$/, "");
content = content.replace(/^\$/, "").replace(/\$$/, "");
const conversions = [
// Fractions
[/\\frac\{([^}]+)\}\{([^}]+)\}/g, "($1)/($2)"],
// Superscripts and subscripts (already mostly compatible)
// Greek letters (mostly the same, just remove backslash)
[/\\alpha\b/g, "alpha"],
[/\\beta\b/g, "beta"],
[/\\gamma\b/g, "gamma"],
[/\\delta\b/g, "delta"],
[/\\epsilon\b/g, "epsilon"],
[/\\zeta\b/g, "zeta"],
[/\\eta\b/g, "eta"],
[/\\theta\b/g, "theta"],
[/\\iota\b/g, "iota"],
[/\\kappa\b/g, "kappa"],
[/\\lambda\b/g, "lambda"],
[/\\mu\b/g, "mu"],
[/\\nu\b/g, "nu"],
[/\\xi\b/g, "xi"],
[/\\pi\b/g, "pi"],
[/\\rho\b/g, "rho"],
[/\\sigma\b/g, "sigma"],
[/\\tau\b/g, "tau"],
[/\\phi\b/g, "phi"],
[/\\chi\b/g, "chi"],
[/\\psi\b/g, "psi"],
[/\\omega\b/g, "omega"],
// Capital Greek
[/\\Gamma\b/g, "Gamma"],
[/\\Delta\b/g, "Delta"],
[/\\Theta\b/g, "Theta"],
[/\\Lambda\b/g, "Lambda"],
[/\\Xi\b/g, "Xi"],
[/\\Pi\b/g, "Pi"],
[/\\Sigma\b/g, "Sigma"],
[/\\Phi\b/g, "Phi"],
[/\\Psi\b/g, "Psi"],
[/\\Omega\b/g, "Omega"],
// Common functions
[/\\sin\b/g, "sin"],
[/\\cos\b/g, "cos"],
[/\\tan\b/g, "tan"],
[/\\log\b/g, "log"],
[/\\ln\b/g, "ln"],
[/\\exp\b/g, "exp"],
// Sums and integrals
[/\\sum/g, "sum"],
[/\\prod/g, "product"],
[/\\int/g, "integral"],
[/\\infty\b/g, "oo"],
// Arrows
[/\\rightarrow\b/g, "->"],
[/\\leftarrow\b/g, "<-"],
[/\\Rightarrow\b/g, "=>"],
[/\\Leftarrow\b/g, "<="],
// Operators
[/\\times\b/g, "times"],
[/\\cdot\b/g, "dot"],
[/\\pm\b/g, "plus.minus"],
[/\\mp\b/g, "minus.plus"],
// Special sets
[/\\mathbb\{R\}/g, "RR"],
[/\\mathbb\{N\}/g, "NN"],
[/\\mathbb\{Z\}/g, "ZZ"],
[/\\mathbb\{Q\}/g, "QQ"],
[/\\mathbb\{C\}/g, "CC"],
// Limits
[/\\lim/g, "lim"],
[/\\to\b/g, "->"],
// Parentheses (mostly the same)
[/\\left\(/g, "("],
[/\\right\)/g, ")"],
[/\\left\[/g, "["],
[/\\right\]/g, "]"],
[/\\left\{/g, "{"],
[/\\right\}/g, "}"],
// Sqrt
[/\\sqrt\{([^}]+)\}/g, "sqrt($1)"],
// Text
[/\\text\{([^}]+)\}/g, '"$1"']
];
for (const [pattern, replacement] of conversions) {
content = content.replace(pattern, replacement);
}
return content.trim();
}
async renderTypstMath(mathContent, container, blockId, isBlock) {
if (this.renderCache.has(blockId)) {
container.innerHTML = this.renderCache.get(blockId);
return;
}
try {
const wrappedContent = `$ ${mathContent} $`;
const typstContent = this.wrapInTypstDocument(wrappedContent, isBlock);
const typstFile = path.join(this.tempDir, `${blockId}.typ`);
const htmlFile = path.join(this.tempDir, `${blockId}.html`);
this.pendingTypstContent.set(blockId, typstContent);
await import_fs.promises.writeFile(typstFile, typstContent, "utf-8");
await this.renderTypstToHtml(typstFile, htmlFile, container, blockId);
} catch (error) {
container.innerHTML = this.formatError(error, true);
if (this.settings.debugMode) {
console.error("Typst rendering error:", error);
}
}
}
// Synchronously compile a single block (used by prepareExport)
async syncCompileBlock(blockId) {
try {
const typstContent = this.pendingTypstContent.get(blockId);
if (!typstContent)
return;
const typstFile = path.join(this.tempDir, `${blockId}.typ`);
const htmlFile = path.join(this.tempDir, `${blockId}.html`);
(0, import_fs.writeFileSync)(typstFile, typstContent, "utf-8");
const args = [
"compile",
typstFile,
htmlFile,
"--features",
"html",
"--format",
"html"
];
if (this.settings.debugMode) {
console.log("Typst sync compile (prepareExport):", this.settings.typstPath, args.join(" "));
}
const res = (0, import_child_process.spawnSync)(this.settings.typstPath, args, { encoding: "utf-8" });
if (res.error || res.status !== 0) {
const errStr = res.stderr || res.error && res.error.message || `Typst exited ${res.status}`;
throw new Error(errStr);
}
const html = (0, import_fs.readFileSync)(htmlFile, "utf-8");
const bodyMatch = html.match(/<body[^>]*>([\s\S]*)<\/body>/i);
const content = bodyMatch ? bodyMatch[1] : html;
this.renderCache.set(blockId, content);
try {
const el = document.querySelector(`[data-typst-blockid="${blockId}"]`);
if (el instanceof HTMLElement)
el.innerHTML = content;
} catch (e) {
}
} catch (error) {
if (this.settings.debugMode)
console.error("Error in syncCompileBlock:", error);
throw error;
}
}
// Prepare the document for export by sync-compiling all pending Typst blocks
async prepareExport() {
const pending = Array.from(this.pendingTypstContent.keys());
if (pending.length === 0) {
new import_obsidian.Notice("No pending Typst math blocks to prepare");
return;
}
new import_obsidian.Notice(`Preparing ${pending.length} Typst math blocks for export...`);
const prevForce = this.settings.forceSyncCompile;
const prevSyncOnly = this.settings.syncOnlyDuringExport;
this.settings.forceSyncCompile = true;
this.settings.syncOnlyDuringExport = false;
this.exportInProgress = true;
let success = 0;
for (const id of pending) {
try {
await this.syncCompileBlock(id);
success++;
} catch (e) {
console.error("prepareExport: failed to compile block", id, e);
}
}
this.settings.forceSyncCompile = prevForce;
this.settings.syncOnlyDuringExport = prevSyncOnly;
this.exportInProgress = false;
new import_obsidian.Notice(`Prepared ${success}/${pending.length} Typst math blocks for export`);
}
wrapInTypstDocument(mathContent, isBlock = true) {
const sizeConfig = isBlock ? "16pt" : "14pt";
return `
#set text(size: ${sizeConfig})
#show math.equation: html.frame
#show math.equation.where(block: false): box
${mathContent}
`;
}
generateBlockId(source, sourcePath) {
const hash = this.simpleHash(source + sourcePath);
return `block-${hash}`;
}
simpleHash(str) {
let hash = 0;
for (let i = 0; i < str.length; i++) {
const char = str.charCodeAt(i);
hash = (hash << 5) - hash + char;
hash = hash & hash;
}
return Math.abs(hash).toString(36);
}
/**
* Parse the full Typst-generated HTML and return a cleaned HTML string.
* - Merges paginated pages if present
* - Removes placeholder ellipsis-only nodes
* - Strips scripts/styles that could interfere
*/
parseTypstHtml(html) {
try {
const parser = new DOMParser();
const doc = parser.parseFromString(html, "text/html");
doc.querySelectorAll("script, style").forEach((n) => n.remove());
const pageSelectors = [".typst-page", ".page", ".typst-doc", "#content", "body > div"];
let parts = [];
const typstDoc = doc.querySelector(".typst-doc");
if (typstDoc) {
const children = Array.from(typstDoc.children);
if (children.length > 1) {
for (const c of children)
parts.push(c.innerHTML);
} else {
parts.push(typstDoc.innerHTML);
}
} else {
for (const sel of pageSelectors) {
const nodes = Array.from(doc.querySelectorAll(sel));
if (nodes.length > 1) {
for (const n of nodes)
parts.push(n.innerHTML);
break;
} else if (nodes.length === 1 && parts.length === 0) {
parts.push(nodes[0].innerHTML);
}
}
}
if (parts.length === 0) {
const bodyHtml = doc.body ? doc.body.innerHTML : html;
parts = [bodyHtml];
}
parts = parts.map((p) => {
var _a;
const partDoc = parser.parseFromString(`<div>${p}</div>`, "text/html");
const walker = document.createTreeWalker(partDoc.body, NodeFilter.SHOW_ELEMENT, null);
const toRemove = [];
let node = walker.nextNode();
while (node) {
const el = node;
const txt = (_a = el.textContent) == null ? void 0 : _a.trim();
if (txt && (/^\.{3,}$/.test(txt) || /^…+$/.test(txt) || txt === "\u22EF")) {
toRemove.push(el);
}
node = walker.nextNode();
}
toRemove.forEach((e) => e.remove());
return partDoc.body.innerHTML;
});
const joined = parts.map((p, i) => `<div class="typst-merged-page">${p}</div>`).join('\n<hr class="typst-page-break">\n');
return joined;
} catch (e) {
return html;
}
}
async renderTypstToHtml(typstFile, htmlFile, container, blockId) {
const useSync = this.settings.forceSyncCompile || this.settings.syncOnlyDuringExport && this.exportInProgress;
if (useSync) {
try {
const args = [
"compile",
typstFile,
htmlFile,
"--features",
"html",
"--format",
"html"
];
if (this.settings.debugMode) {
console.log("Running Typst (sync):", this.settings.typstPath, args.join(" "));
}
const res = (0, import_child_process.spawnSync)(this.settings.typstPath, args, { encoding: "utf-8" });
if (res.error) {
const errMsg = res.error.message || String(res.error);
container.innerHTML = this.formatError(errMsg, false);
throw res.error;
}
if (res.status !== 0) {
const errStr = res.stderr || `Typst process exited with code ${res.status}`;
container.innerHTML = this.formatError(errStr, false);
throw new Error(errStr);
}
const html = (0, import_fs.readFileSync)(htmlFile, "utf-8");
const bodyMatch = html.match(/<body[^>]*>([\s\S]*)<\/body>/i);
const content = bodyMatch ? bodyMatch[1] : html;
this.renderCache.set(blockId, content);
container.innerHTML = content;
return Promise.resolve();
} catch (err) {
if (this.settings.debugMode)
console.error("Typst sync render error:", err);
return Promise.reject(err);
}
}
return new Promise((resolve, reject) => {
var _a;
if (this.watchers.has(blockId)) {
(_a = this.watchers.get(blockId)) == null ? void 0 : _a.kill();
this.watchers.delete(blockId);
}
const args = [
"compile",
typstFile,
htmlFile,
"--features",
"html",
"--format",
"html"
];
if (this.settings.debugMode) {
console.log(
"Running Typst command:",
this.settings.typstPath,
args.join(" ")
);
}
const typstProcess = (0, import_child_process.spawn)(this.settings.typstPath, args);
let stderr = "";
typstProcess.stderr.on("data", (data) => {
stderr += data.toString();
});
typstProcess.on("close", async (code) => {
if (code === 0) {
try {
const html = await import_fs.promises.readFile(htmlFile, "utf-8");
const bodyMatch = html.match(/<body[^>]*>([\s\S]*)<\/body>/i);
const content = bodyMatch ? bodyMatch[1] : html;
this.renderCache.set(blockId, content);
container.innerHTML = content;
resolve();
} catch (error) {
container.innerHTML = this.formatError(error, false);
reject(error);
}
} else {
const errorMsg = stderr || `Typst process exited with code ${code}`;
container.innerHTML = this.formatError(errorMsg, false);
reject(new Error(errorMsg));
}
});
typstProcess.on("error", (error) => {
container.innerHTML = this.formatError(error, false);
reject(error);
});
});
}
async renderTypstWithWatch(typstFile, htmlFile, container, blockId) {
return new Promise((resolve, reject) => {
var _a;
if (this.watchers.has(blockId)) {
(_a = this.watchers.get(blockId)) == null ? void 0 : _a.kill();
this.watchers.delete(blockId);
}
const args = [
"watch",
typstFile,
htmlFile,
"--features",
"html",
"--format",
"html"
];
if (this.settings.debugMode) {
console.log(
"Running Typst watch:",
this.settings.typstPath,
args.join(" ")
);
}
const typstProcess = (0, import_child_process.spawn)(this.settings.typstPath, args);
this.watchers.set(blockId, typstProcess);
let stderr = "";
let hasRendered = false;
const checkForUpdates = async () => {
try {
const html = await import_fs.promises.readFile(htmlFile, "utf-8");
const bodyMatch = html.match(/<body[^>]*>([\s\S]*)<\/body>/i);
const content = bodyMatch ? bodyMatch[1] : html;
this.renderCache.set(blockId, content);
container.innerHTML = content;
if (!hasRendered) {
hasRendered = true;
resolve();
}
} catch (error) {
if (this.settings.debugMode) {
console.log("Waiting for Typst to generate output...");
}
}
};
typstProcess.stderr.on("data", (data) => {
stderr += data.toString();
if (stderr.includes("error:")) {
container.innerHTML = this.formatError(stderr, false);
}
});
typstProcess.stdout.on("data", (data) => {
const output = data.toString();
if (this.settings.debugMode) {
console.log("Typst watch output:", output);
}
if (output.includes("written to") || output.includes("compiled")) {
setTimeout(checkForUpdates, 100);
}
});
setTimeout(checkForUpdates, 500);
typstProcess.on("close", (code) => {
this.watchers.delete(blockId);
if (code !== 0 && code !== null && !hasRendered) {
const errorMsg = stderr || `Typst watch exited with code ${code}`;
container.innerHTML = this.formatError(errorMsg, false);
reject(new Error(errorMsg));
}
});
typstProcess.on("error", (error) => {
this.watchers.delete(blockId);
container.innerHTML = this.formatError(error, false);
reject(error);
});
});
}
};
var TypstMathSettingTab = class extends import_obsidian.PluginSettingTab {
constructor(app, plugin) {
super(app, plugin);
this.plugin = plugin;
}
display() {
const { containerEl } = this;
containerEl.empty();
containerEl.createEl("h2", { text: "Typst Math Renderer Settings" });
new import_obsidian.Setting(containerEl).setName("Typst CLI path").setDesc('Path to the Typst executable (e.g., "typst" or full path)').addText(
(text) => text.setPlaceholder("typst").setValue(this.plugin.settings.typstPath).onChange(async (value) => {
this.plugin.settings.typstPath = value;
await this.plugin.saveSettings();
})
);
new import_obsidian.Setting(containerEl).setName("Enable inline math").setDesc("Process inline math blocks ($...$)").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.enableInlineMath).onChange(async (value) => {
this.plugin.settings.enableInlineMath = value;
await this.plugin.saveSettings();
})
);
new import_obsidian.Setting(containerEl).setName("Enable display math").setDesc("Process display math blocks ($$...$$)").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.enableDisplayMath).onChange(async (value) => {
this.plugin.settings.enableDisplayMath = value;
await this.plugin.saveSettings();
})
);
new import_obsidian.Setting(containerEl).setName("Debug mode").setDesc("Enable debug logging in the console").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.debugMode).onChange(async (value) => {
this.plugin.settings.debugMode = value;
await this.plugin.saveSettings();
})
);
new import_obsidian.Setting(containerEl).setName("Show detailed errors").setDesc("Display detailed Typst error messages (useful for debugging)").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.showDetailedErrors).onChange(async (value) => {
this.plugin.settings.showDetailedErrors = value;
await this.plugin.saveSettings();
})
);
new import_obsidian.Setting(containerEl).setName("Use watch mode").setDesc("Enable watch mode for code blocks (auto-recompile on changes) - experimental").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.useWatchMode).onChange(async (value) => {
this.plugin.settings.useWatchMode = value;
await this.plugin.saveSettings();
})
);
new import_obsidian.Setting(containerEl).setName("Force synchronous compile (for exports)").setDesc("Use a synchronous Typst compile which can improve reliability when exporting to PDF. This will block rendering while Typst runs.").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.forceSyncCompile).onChange(async (value) => {
this.plugin.settings.forceSyncCompile = value;
await this.plugin.saveSettings();
new import_obsidian.Notice("Force sync compile setting updated");
})
);
new import_obsidian.Setting(containerEl).setName("Sync only during print/export").setDesc("If enabled, synchronous compilation will only be used during a print/export flow. Disable to always use sync compilation when enabled.").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.syncOnlyDuringExport).onChange(async (value) => {
this.plugin.settings.syncOnlyDuringExport = value;
await this.plugin.saveSettings();
new import_obsidian.Notice("Sync-only-during-export setting updated");
})
);
new import_obsidian.Setting(containerEl).setName("Enable live preview").setDesc("Render Typst math in live preview mode (works automatically via MathJax override)").addToggle(
(toggle) => toggle.setValue(this.plugin.settings.enableLivePreview).onChange(async (value) => {
this.plugin.settings.enableLivePreview = value;
await this.plugin.saveSettings();
new import_obsidian.Notice("Reload Obsidian for this change to take full effect");
})
);
containerEl.createEl("h3", { text: "Usage" });
containerEl.createEl("p", {
text: "Create a code block with ```math or ```typst and write your Typst math syntax inside."
});
containerEl.createEl("pre", {
text: "```math\n$ sum_(i=1)^n i = (n(n+1))/2 $\n```"
});
containerEl.createEl("h3", { text: "Installation" });
containerEl.createEl("p", {
text: "Make sure you have Typst CLI installed. Visit https://github.com/typst/typst for installation instructions."
});
}
};