chore(client/ts): port mermaid

This commit is contained in:
Elian Doran 2025-01-16 18:20:23 +02:00
parent c8b745bc6a
commit 1e182f5820
No known key found for this signature in database

View File

@ -4,6 +4,8 @@ import NoteContextAwareWidget from "./note_context_aware_widget.js";
import server from "../services/server.js"; import server from "../services/server.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import { loadElkIfNeeded } from "../services/mermaid.js"; import { loadElkIfNeeded } from "../services/mermaid.js";
import type FNote from "../entities/fnote.js";
import type { EventData } from "../components/app_context.js";
const TPL = `<div class="mermaid-widget"> const TPL = `<div class="mermaid-widget">
<style> <style>
@ -39,8 +41,14 @@ const TPL = `<div class="mermaid-widget">
let idCounter = 1; let idCounter = 1;
export default class MermaidWidget extends NoteContextAwareWidget { export default class MermaidWidget extends NoteContextAwareWidget {
private $display!: JQuery<HTMLElement>;
private $errorContainer!: JQuery<HTMLElement>;
private $errorMessage!: JQuery<HTMLElement>;
private dirtyAttachment?: boolean;
isEnabled() { isEnabled() {
return super.isEnabled() && this.note?.type === "mermaid" && this.note.isContentAvailable() && this.noteContext?.viewScope.viewMode === "default"; return super.isEnabled() && this.note?.type === "mermaid" && this.note.isContentAvailable() && this.noteContext?.viewScope?.viewMode === "default";
} }
doRender() { doRender() {
@ -51,14 +59,14 @@ export default class MermaidWidget extends NoteContextAwareWidget {
this.$errorMessage = this.$errorContainer.find(".error-content"); this.$errorMessage = this.$errorContainer.find(".error-content");
} }
async refreshWithNote(note) { async refreshWithNote(note: FNote) {
this.$errorContainer.hide(); this.$errorContainer.hide();
await libraryLoader.requireLibrary(libraryLoader.MERMAID); await libraryLoader.requireLibrary(libraryLoader.MERMAID);
mermaid.mermaidAPI.initialize({ mermaid.mermaidAPI.initialize({
startOnLoad: false, startOnLoad: false,
...getMermaidConfig() ...getMermaidConfig() as any
}); });
this.$display.empty(); this.$display.empty();
@ -91,12 +99,11 @@ export default class MermaidWidget extends NoteContextAwareWidget {
this.$display.attr("id", `mermaid-render-${idCounter}`); this.$display.attr("id", `mermaid-render-${idCounter}`);
WZoom.create(`#mermaid-render-${idCounter}`, { WZoom.create(`#mermaid-render-${idCounter}`, {
type: "html",
maxScale: 50, maxScale: 50,
speed: 1.3, speed: 1.3,
zoomOnClick: false zoomOnClick: false
}); });
} catch (e) { } catch (e: any) {
console.warn(e); console.warn(e);
this.$errorMessage.text(e.message); this.$errorMessage.text(e.message);
this.$errorContainer.show(); this.$errorContainer.show();
@ -106,24 +113,28 @@ export default class MermaidWidget extends NoteContextAwareWidget {
async renderSvg() { async renderSvg() {
idCounter++; idCounter++;
if (!this.note) {
return "";
}
const blob = await this.note.getBlob(); const blob = await this.note.getBlob();
const content = blob.content || ""; const content = blob?.content || "";
await loadElkIfNeeded(content); await loadElkIfNeeded(content);
const { svg } = await mermaid.mermaidAPI.render(`mermaid-graph-${idCounter}`, content); const { svg } = await mermaid.mermaidAPI.render(`mermaid-graph-${idCounter}`, content);
return svg; return svg;
} }
async entitiesReloadedEvent({ loadResults }) { async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
if (loadResults.isNoteContentReloaded(this.noteId)) { if (this.noteId && loadResults.isNoteContentReloaded(this.noteId)) {
this.dirtyAttachment = true; this.dirtyAttachment = true;
await this.refresh(); await this.refresh();
} }
} }
async exportSvgEvent({ ntxId }) { async exportSvgEvent({ ntxId }: EventData<"exportSvg">) {
if (!this.isNoteContext(ntxId) || this.note.type !== "mermaid") { if (!this.isNoteContext(ntxId) || this.note?.type !== "mermaid") {
return; return;
} }
@ -139,6 +150,7 @@ export function getMermaidConfig() {
return { return {
theme: mermaidTheme.trim(), theme: mermaidTheme.trim(),
securityLevel: "antiscript", securityLevel: "antiscript",
// TODO: Are all these options correct?
flow: { useMaxWidth: false }, flow: { useMaxWidth: false },
sequence: { useMaxWidth: false }, sequence: { useMaxWidth: false },
gantt: { useMaxWidth: false }, gantt: { useMaxWidth: false },