feat(client): integrate katex

This commit is contained in:
Elian Doran 2025-05-17 10:31:31 +03:00
parent 255e529714
commit 7b787fff59
No known key found for this signature in database
13 changed files with 30 additions and 34 deletions

View File

@ -40,6 +40,7 @@
"jquery-hotkeys": "0.2.2", "jquery-hotkeys": "0.2.2",
"jquery.fancytree": "2.38.5", "jquery.fancytree": "2.38.5",
"jsplumb": "2.15.6", "jsplumb": "2.15.6",
"katex": "0.16.22",
"knockout": "3.5.1", "knockout": "3.5.1",
"leaflet": "1.9.4", "leaflet": "1.9.4",
"leaflet-gpx": "2.2.0", "leaflet-gpx": "2.2.0",

View File

@ -1,7 +1,6 @@
import renderService from "./render.js"; import renderService from "./render.js";
import protectedSessionService from "./protected_session.js"; import protectedSessionService from "./protected_session.js";
import protectedSessionHolder from "./protected_session_holder.js"; import protectedSessionHolder from "./protected_session_holder.js";
import libraryLoader from "./library_loader.js";
import openService from "./open.js"; import openService from "./open.js";
import froca from "./froca.js"; import froca from "./froca.js";
import utils from "./utils.js"; import utils from "./utils.js";
@ -16,6 +15,7 @@ import { normalizeMimeTypeForCKEditor } from "./mime_type_definitions.js";
import renderDoc from "./doc_renderer.js"; import renderDoc from "./doc_renderer.js";
import { t } from "../services/i18n.js"; import { t } from "../services/i18n.js";
import WheelZoom from 'vanilla-js-wheel-zoom'; import WheelZoom from 'vanilla-js-wheel-zoom';
import { renderMathInElement } from "./math.js";
let idCounter = 1; let idCounter = 1;
@ -94,8 +94,6 @@ async function renderText(note: FNote | FAttachment, $renderedContent: JQuery<HT
$renderedContent.append($('<div class="ck-content">').html(blob.content)); $renderedContent.append($('<div class="ck-content">').html(blob.content));
if ($renderedContent.find("span.math-tex").length > 0) { if ($renderedContent.find("span.math-tex").length > 0) {
await libraryLoader.requireLibrary(libraryLoader.KATEX);
renderMathInElement($renderedContent[0], { trust: true }); renderMathInElement($renderedContent[0], { trust: true });
} }

View File

@ -121,6 +121,5 @@ export default {
requireCss, requireCss,
requireLibrary, requireLibrary,
loadHighlightingTheme, loadHighlightingTheme,
KATEX,
HIGHLIGHT_JS HIGHLIGHT_JS
}; };

View File

@ -0,0 +1,5 @@
import katex from "katex";
import "katex/contrib/mhchem";
import "katex/dist/katex.min.css";
export { default as renderMathInElement } from "katex/contrib/auto-render";
export default katex;

View File

@ -24,3 +24,10 @@ declare module "draggabilly" {
declare module "@mind-elixir/node-menu" { declare module "@mind-elixir/node-menu" {
export default mindmap; export default mindmap;
} }
declare module "katex/contrib/auto-render" {
var renderMathInElement: (element: HTMLElement, options: {
trust: boolean;
}) => void;
export default renderMathInElement;
}

View File

@ -131,15 +131,6 @@ declare global {
language: string language: string
}); });
}; };
var renderMathInElement: (element: HTMLElement, options: {
trust: boolean;
}) => void;
var katex: {
renderToString(text: string, opts: {
throwOnError: boolean
});
}
/* /*
* Panzoom * Panzoom

View File

@ -3,7 +3,6 @@ import utils from "../../services/utils.js";
import server from "../../services/server.js"; import server from "../../services/server.js";
import toastService from "../../services/toast.js"; import toastService from "../../services/toast.js";
import appContext from "../../components/app_context.js"; import appContext from "../../components/app_context.js";
import libraryLoader from "../../services/library_loader.js";
import openService from "../../services/open.js"; import openService from "../../services/open.js";
import protectedSessionHolder from "../../services/protected_session_holder.js"; import protectedSessionHolder from "../../services/protected_session_holder.js";
import BasicWidget from "../basic_widget.js"; import BasicWidget from "../basic_widget.js";
@ -12,6 +11,7 @@ import options from "../../services/options.js";
import type FNote from "../../entities/fnote.js"; import type FNote from "../../entities/fnote.js";
import type { NoteType } from "../../entities/fnote.js"; import type { NoteType } from "../../entities/fnote.js";
import { Dropdown, Modal } from "bootstrap"; import { Dropdown, Modal } from "bootstrap";
import { renderMathInElement } from "../../services/math.js";
const TPL = /*html*/` const TPL = /*html*/`
<div class="revisions-dialog modal fade mx-auto" tabindex="-1" role="dialog"> <div class="revisions-dialog modal fade mx-auto" tabindex="-1" role="dialog">
@ -315,8 +315,6 @@ export default class RevisionsDialog extends BasicWidget {
this.$content.html(`<div class="ck-content">${fullRevision.content}</div>`); this.$content.html(`<div class="ck-content">${fullRevision.content}</div>`);
if (this.$content.find("span.math-tex").length > 0) { if (this.$content.find("span.math-tex").length > 0) {
await libraryLoader.requireLibrary(libraryLoader.KATEX);
renderMathInElement(this.$content[0], { trust: true }); renderMathInElement(this.$content[0], { trust: true });
} }
} else if (revisionItem.type === "code") { } else if (revisionItem.type === "code") {

View File

@ -11,8 +11,8 @@ import RightPanelWidget from "./right_panel_widget.js";
import options from "../services/options.js"; import options from "../services/options.js";
import OnClickButtonWidget from "./buttons/onclick_button.js"; import OnClickButtonWidget from "./buttons/onclick_button.js";
import appContext, { type EventData } from "../components/app_context.js"; import appContext, { type EventData } from "../components/app_context.js";
import libraryLoader from "../services/library_loader.js";
import type FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
import katex from "../services/math.js";
const TPL = /*html*/`<div class="highlights-list-widget"> const TPL = /*html*/`<div class="highlights-list-widget">
<style> <style>
@ -175,7 +175,6 @@ export default class HighlightsListWidget extends RightPanelWidget {
} catch (e) { } catch (e) {
if (e instanceof ReferenceError && e.message.includes("katex is not defined")) { if (e instanceof ReferenceError && e.message.includes("katex is not defined")) {
// Load KaTeX if it is not already loaded // Load KaTeX if it is not already loaded
await libraryLoader.requireLibrary(libraryLoader.KATEX);
try { try {
rendered = katex.renderToString(latexCode, { rendered = katex.renderToString(latexCode, {
throwOnError: false throwOnError: false

View File

@ -19,7 +19,7 @@ import RightPanelWidget from "./right_panel_widget.js";
import options from "../services/options.js"; import options from "../services/options.js";
import OnClickButtonWidget from "./buttons/onclick_button.js"; import OnClickButtonWidget from "./buttons/onclick_button.js";
import appContext, { type EventData } from "../components/app_context.js"; import appContext, { type EventData } from "../components/app_context.js";
import libraryLoader from "../services/library_loader.js"; import katex from "../services/math.js";
import type FNote from "../entities/fnote.js"; import type FNote from "../entities/fnote.js";
const TPL = /*html*/`<div class="toc-widget"> const TPL = /*html*/`<div class="toc-widget">
@ -59,7 +59,7 @@ const TPL = /*html*/`<div class="toc-widget">
display: flex; display: flex;
position: relative; position: relative;
list-style: none; list-style: none;
align-items: center; align-items: center;
padding-left: 7px; padding-left: 7px;
cursor: pointer; cursor: pointer;
text-align: justify; text-align: justify;
@ -234,7 +234,6 @@ export default class TocWidget extends RightPanelWidget {
} catch (e) { } catch (e) {
if (e instanceof ReferenceError && e.message.includes("katex is not defined")) { if (e instanceof ReferenceError && e.message.includes("katex is not defined")) {
// Load KaTeX if it is not already loaded // Load KaTeX if it is not already loaded
await libraryLoader.requireLibrary(libraryLoader.KATEX);
try { try {
rendered = katex.renderToString(latexCode, { rendered = katex.renderToString(latexCode, {
throwOnError: false throwOnError: false
@ -277,13 +276,13 @@ export default class TocWidget extends RightPanelWidget {
let curLevel = 2; let curLevel = 2;
const $ols = [$toc]; const $ols = [$toc];
let $previousLi: JQuery<HTMLElement> | undefined; let $previousLi: JQuery<HTMLElement> | undefined;
if (!(this.noteContext?.viewScope?.tocCollapsedHeadings instanceof Set)) { if (!(this.noteContext?.viewScope?.tocCollapsedHeadings instanceof Set)) {
this.noteContext!.viewScope!.tocCollapsedHeadings = new Set<string>(); this.noteContext!.viewScope!.tocCollapsedHeadings = new Set<string>();
} }
const tocCollapsedHeadings = this.noteContext!.viewScope!.tocCollapsedHeadings as Set<string>; const tocCollapsedHeadings = this.noteContext!.viewScope!.tocCollapsedHeadings as Set<string>;
const validHeadingKeys = new Set<string>(); // Used to clean up obsolete entries in tocCollapsedHeadings const validHeadingKeys = new Set<string>(); // Used to clean up obsolete entries in tocCollapsedHeadings
let headingCount = 0; let headingCount = 0;
for (let m = null, headingIndex = 0; (m = headingTagsRegex.exec(html)) !== null; headingIndex++) { for (let m = null, headingIndex = 0; (m = headingTagsRegex.exec(html)) !== null; headingIndex++) {
// //
@ -301,7 +300,7 @@ export default class TocWidget extends RightPanelWidget {
if ($previousLi) { if ($previousLi) {
const headingKey = `h${newLevel}_${headingIndex}_${$previousLi?.text().trim()}`; const headingKey = `h${newLevel}_${headingIndex}_${$previousLi?.text().trim()}`;
this.setupCollapsibleHeading($ol, $previousLi, headingKey, tocCollapsedHeadings, validHeadingKeys); this.setupCollapsibleHeading($ol, $previousLi, headingKey, tocCollapsedHeadings, validHeadingKeys);
} }
} }
} else if (levelDelta < 0) { } else if (levelDelta < 0) {
// Close as many lists as curLevel - newLevel // Close as many lists as curLevel - newLevel
@ -406,7 +405,7 @@ export default class TocWidget extends RightPanelWidget {
const willCollapse = !$previousLi.hasClass("collapsed"); const willCollapse = !$previousLi.hasClass("collapsed");
$previousLi.addClass("animating"); $previousLi.addClass("animating");
if (willCollapse) { // Collapse if (willCollapse) { // Collapse
$ol.css("maxHeight", `${$ol.prop("scrollHeight")}px`); $ol.css("maxHeight", `${$ol.prop("scrollHeight")}px`);
requestAnimationFrame(() => { requestAnimationFrame(() => {
requestAnimationFrame(() => { requestAnimationFrame(() => {

View File

@ -310,7 +310,9 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
math: { math: {
engine: "katex", engine: "katex",
outputType: "span", // or script outputType: "span", // or script
lazyLoad: async () => await libraryLoader.requireLibrary(libraryLoader.KATEX), lazyLoad: async () => {
(window as any).katex = (await import("../../services/math.js")).default
},
forceOutputType: false, // forces output to use outputType forceOutputType: false, // forces output to use outputType
enablePreview: true // Enable preview view enablePreview: true // Enable preview view
}, },

View File

@ -1,11 +1,11 @@
import AbstractTextTypeWidget from "./abstract_text_type_widget.js"; import AbstractTextTypeWidget from "./abstract_text_type_widget.js";
import libraryLoader from "../../services/library_loader.js";
import { applySyntaxHighlight } from "../../services/syntax_highlight.js"; import { applySyntaxHighlight } from "../../services/syntax_highlight.js";
import type FNote from "../../entities/fnote.js"; import type FNote from "../../entities/fnote.js";
import type { CommandListenerData, EventData } from "../../components/app_context.js"; import type { CommandListenerData, EventData } from "../../components/app_context.js";
import { getLocaleById } from "../../services/i18n.js"; import { getLocaleById } from "../../services/i18n.js";
import appContext from "../../components/app_context.js"; import appContext from "../../components/app_context.js";
import { getMermaidConfig } from "../../services/mermaid.js"; import { getMermaidConfig } from "../../services/mermaid.js";
import { renderMathInElement } from "../../services/math.js";
const TPL = /*html*/` const TPL = /*html*/`
<div class="note-detail-readonly-text note-detail-printable" tabindex="100"> <div class="note-detail-readonly-text note-detail-printable" tabindex="100">
@ -121,8 +121,6 @@ export default class ReadOnlyTextTypeWidget extends AbstractTextTypeWidget {
}); });
if (this.$content.find("span.math-tex").length > 0) { if (this.$content.find("span.math-tex").length > 0) {
await libraryLoader.requireLibrary(libraryLoader.KATEX);
renderMathInElement(this.$content[0], { trust: true }); renderMathInElement(this.$content[0], { trust: true });
} }

View File

@ -42,8 +42,7 @@
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@types/xml2js": "0.4.14", "@types/xml2js": "0.4.14",
"express-http-proxy": "2.1.1", "express-http-proxy": "2.1.1",
"jquery": "3.7.1", "jquery": "3.7.1",
"katex": "0.16.22",
"@anthropic-ai/sdk": "0.51.0", "@anthropic-ai/sdk": "0.51.0",
"@braintree/sanitize-url": "7.1.1", "@braintree/sanitize-url": "7.1.1",
"@triliumnext/commons": "workspace:*", "@triliumnext/commons": "workspace:*",

6
pnpm-lock.yaml generated
View File

@ -257,6 +257,9 @@ importers:
jsplumb: jsplumb:
specifier: 2.15.6 specifier: 2.15.6
version: 2.15.6 version: 2.15.6
katex:
specifier: 0.16.22
version: 0.16.22
knockout: knockout:
specifier: 3.5.1 specifier: 3.5.1
version: 3.5.1 version: 3.5.1
@ -725,9 +728,6 @@ importers:
jsdom: jsdom:
specifier: 26.1.0 specifier: 26.1.0
version: 26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5) version: 26.1.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)
katex:
specifier: 0.16.22
version: 0.16.22
marked: marked:
specifier: 15.0.11 specifier: 15.0.11
version: 15.0.11 version: 15.0.11