Add katex support

This commit is contained in:
Ashish Jain 2024-06-20 13:21:48 +02:00
parent 1096b185ee
commit e07fdfedb6
7 changed files with 70 additions and 36 deletions

View File

@ -8,6 +8,8 @@ import { markdownToHTML, markdownToLines } from '../rendering-util/handle-markdo
import { decodeEntities } from '../utils.js';
import { splitLineToFitWidth } from './splitText.js';
import type { MarkdownLine, MarkdownWord } from './types.js';
import common, { renderKatex } from '$root/diagrams/common/common.js';
import { getConfig } from '$root/diagram-api/diagramAPI.js';
function applyStyle(dom, styleFn) {
if (styleFn) {
@ -15,11 +17,16 @@ function applyStyle(dom, styleFn) {
}
}
function addHtmlSpan(element, node, width, classes, addBackground = false) {
async function addHtmlSpan(element, node, width, classes, addBackground = false) {
const fo = element.append('foreignObject');
const div = fo.append('xhtml:div');
const label = node.label;
// const label = node.label;
let label = '';
if (node.label) {
label = await renderKatex(node.label.replace(common.lineBreakRegex, '\n'), getConfig());
}
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
div.html(
`<span class="${labelClass} ${classes}" ` +
@ -184,7 +191,7 @@ export function replaceIconSubstring(text: string) {
// Note when using from flowcharts converting the API isNode means classes should be set accordingly. When using htmlLabels => to sett classes to'nodeLabel' when isNode=true otherwise 'edgeLabel'
// When not using htmlLabels => to set classes to 'title-row' when isTitle=true otherwise 'title-row'
export const createText = (
export const createText = async (
el,
text = '',
{
@ -218,7 +225,7 @@ export const createText = (
label: decodedReplacedText,
labelStyle: style.replace('fill:', 'color:'),
};
const vertexNode = addHtmlSpan(el, node, width, classes, addSvgBackground);
const vertexNode = await addHtmlSpan(el, node, width, classes, addSvgBackground);
return vertexNode;
} else {
const structuredText = markdownToLines(text, config);

View File

@ -120,20 +120,36 @@ const recursiveRender = async (_elem, graph, diagramType, id, parentCluster, sit
})
);
// Insert labels, this will insert them into the dom so that the width can be calculated
// Also figure out which edges point to/from clusters and adjust them accordingly
// Edges from/to clusters really points to the first child in the cluster.
// TODO: pick optimal child in the cluster to us as link anchor
graph.edges().forEach(function (e) {
const processEdges = async () => {
const edgePromises = graph.edges().map(async function (e) {
const edge = graph.edge(e.v, e.w, e.name);
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
// Check if link is either from or to a cluster
log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]);
insertEdgeLabel(edgeLabels, edge);
await insertEdgeLabel(edgeLabels, edge);
});
await Promise.all(edgePromises);
};
await processEdges();
// // Insert labels, this will insert them into the dom so that the width can be calculated
// // Also figure out which edges point to/from clusters and adjust them accordingly
// // Edges from/to clusters really points to the first child in the cluster.
// // TODO: pick optimal child in the cluster to us as link anchor
// await graph.edges().forEach(async function (e) {
// const edge = graph.edge(e.v, e.w, e.name);
// log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
// log.info('Edge ' + e.v + ' -> ' + e.w + ': ', e, ' ', JSON.stringify(graph.edge(e)));
// // Check if link is either from or to a cluster
// log.info('Fix', clusterDb, 'ids:', e.v, e.w, 'Translating: ', clusterDb[e.v], clusterDb[e.w]);
// await insertEdgeLabel(edgeLabels, edge);
// });
graph.edges().forEach(function (e) {
log.info('Edge ' + e.v + ' -> ' + e.w + ': ' + JSON.stringify(e));
});

View File

@ -10,7 +10,7 @@ import createLabel from './createLabel.js';
import { createRoundedRectPathD } from './shapes/roundedRectPath.ts';
import { userNodeOverrides } from '$root/rendering-util/rendering-elements/shapes/handdrawnStyles.js';
const rect = (parent, node) => {
const rect = async (parent, node) => {
log.info('Creating subgraph rect for ', node.id, node);
const siteConfig = getConfig();
const { themeVariables, handdrawnSeed } = siteConfig;
@ -29,8 +29,8 @@ const rect = (parent, node) => {
// .appendChild(createLabel(node.label, node.labelStyle, undefined, true));
const text =
node.labelType === 'markdown'
? createText(labelEl, node.label, { style: node.labelStyle, useHtmlLabels })
: labelEl.node().appendChild(createLabel(node.label, node.labelStyle, undefined, true));
? await createText(labelEl, node.label, { style: node.labelStyle, useHtmlLabels })
: labelEl.node().appendChild(await createLabel(node.label, node.labelStyle, undefined, true));
// Get the size of the label
let bbox = text.getBBox();
@ -154,7 +154,7 @@ const noteGroup = (parent, node) => {
return { cluster: shapeSvg, labelBBox: { width: 0, height: 0 } };
};
const roundedWithTitle = (parent, node) => {
const roundedWithTitle = async (parent, node) => {
const siteConfig = getConfig();
const { themeVariables, handdrawnSeed } = siteConfig;
@ -177,7 +177,9 @@ const roundedWithTitle = (parent, node) => {
const label = shapeSvg.insert('g').attr('class', 'cluster-label');
let innerRect = shapeSvg.append('rect');
const text = label.node().appendChild(createLabel(node.label, node.labelStyle, undefined, true));
const text = label
.node()
.appendChild(await createLabel(node.label, node.labelStyle, undefined, true));
// Get the size of the label
let bbox = text.getBBox();

View File

@ -1,7 +1,7 @@
import { select } from 'd3';
import { log } from '$root/logger.js';
import { getConfig } from '$root/diagram-api/diagramAPI.js';
import { evaluate } from '$root/diagrams/common/common.js';
import common, { evaluate, renderKatex } from '$root/diagrams/common/common.js';
import { decodeEntities } from '$root/utils.js';
/**
@ -18,11 +18,14 @@ function applyStyle(dom, styleFn) {
* @param {any} node
* @returns {SVGForeignObjectElement} Node
*/
function addHtmlLabel(node) {
async function addHtmlLabel(node) {
const fo = select(document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject'));
const div = fo.append('xhtml:div');
const label = node.label;
let label = node.label;
if (node.label) {
label = await renderKatex(node.label.replace(common.lineBreakRegex, '\n'), getConfig());
}
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
div.html(
'<span class="' +
@ -49,11 +52,12 @@ function addHtmlLabel(node) {
* @param isNode
* @deprecated svg-util/createText instead
*/
const createLabel = (_vertexText, style, isTitle, isNode) => {
const createLabel = async (_vertexText, style, isTitle, isNode) => {
let vertexText = _vertexText || '';
if (typeof vertexText === 'object') {
vertexText = vertexText[0];
}
if (evaluate(getConfig().flowchart.htmlLabels)) {
// TODO: addHtmlLabel accepts a labelStyle. Do we possibly have that?
vertexText = vertexText.replace(/\\n|\n/g, '<br />');
@ -66,7 +70,7 @@ const createLabel = (_vertexText, style, isTitle, isNode) => {
),
labelStyle: style ? style.replace('fill:', 'color:') : style,
};
let vertexNode = addHtmlLabel(node);
let vertexNode = await addHtmlLabel(node);
// vertexNode.parentNode.removeChild(vertexNode);
return vertexNode;
} else {

View File

@ -19,17 +19,17 @@ export const clear = () => {
terminalLabels = {};
};
export const insertEdgeLabel = (elem, edge) => {
export const insertEdgeLabel = async (elem, edge) => {
const useHtmlLabels = evaluate(getConfig().flowchart.htmlLabels);
// Create the actual text element
const labelElement =
edge.labelType === 'markdown'
? createText(elem, edge.label, {
? await createText(elem, edge.label, {
style: edge.labelStyle,
useHtmlLabels,
addSvgBackground: true,
})
: createLabel(edge.label, edge.labelStyle);
: await createLabel(edge.label, edge.labelStyle);
log.info('abc82', edge, edge.labelType);
// Create outer g, edgeLabel, this will be positioned after graph layout
@ -60,7 +60,7 @@ export const insertEdgeLabel = (elem, edge) => {
let fo;
if (edge.startLabelLeft) {
// Create the actual text element
const startLabelElement = createLabel(edge.startLabelLeft, edge.labelStyle);
const startLabelElement = await createLabel(edge.startLabelLeft, edge.labelStyle);
const startEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
const inner = startEdgeLabelLeft.insert('g').attr('class', 'inner');
fo = inner.node().appendChild(startLabelElement);
@ -74,7 +74,7 @@ export const insertEdgeLabel = (elem, edge) => {
}
if (edge.startLabelRight) {
// Create the actual text element
const startLabelElement = createLabel(edge.startLabelRight, edge.labelStyle);
const startLabelElement = await createLabel(edge.startLabelRight, edge.labelStyle);
const startEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
const inner = startEdgeLabelRight.insert('g').attr('class', 'inner');
fo = startEdgeLabelRight.node().appendChild(startLabelElement);
@ -90,7 +90,7 @@ export const insertEdgeLabel = (elem, edge) => {
}
if (edge.endLabelLeft) {
// Create the actual text element
const endLabelElement = createLabel(edge.endLabelLeft, edge.labelStyle);
const endLabelElement = await createLabel(edge.endLabelLeft, edge.labelStyle);
const endEdgeLabelLeft = elem.insert('g').attr('class', 'edgeTerminals');
const inner = endEdgeLabelLeft.insert('g').attr('class', 'inner');
fo = inner.node().appendChild(endLabelElement);
@ -107,7 +107,7 @@ export const insertEdgeLabel = (elem, edge) => {
}
if (edge.endLabelRight) {
// Create the actual text element
const endLabelElement = createLabel(edge.endLabelRight, edge.labelStyle);
const endLabelElement = await createLabel(edge.endLabelRight, edge.labelStyle);
const endEdgeLabelRight = elem.insert('g').attr('class', 'edgeTerminals');
const inner = endEdgeLabelRight.insert('g').attr('class', 'inner');

View File

@ -34,7 +34,7 @@ export const rectWithTitle = async (parent: SVGElement, node: Node) => {
const title = node.label;
const text = label.node().appendChild(createLabel(title, node.labelStyle, true, true));
const text = label.node().appendChild(await createLabel(title, node.labelStyle, true, true));
let bbox = { width: 0, height: 0 };
if (evaluate(getConfig()?.flowchart?.htmlLabels)) {
const div = text.children[0];
@ -49,7 +49,12 @@ export const rectWithTitle = async (parent: SVGElement, node: Node) => {
const descr = label
.node()
.appendChild(
createLabel(textRows.join ? textRows.join('<br/>') : textRows, node.labelStyle, true, true)
await createLabel(
textRows.join ? textRows.join('<br/>') : textRows,
node.labelStyle,
true,
true
)
);
if (evaluate(getConfig()?.flowchart?.htmlLabels)) {

View File

@ -31,7 +31,7 @@ export const labelHelper = async (parent, node, _classes) => {
}
let text;
text = createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
text = await createText(labelEl, sanitizeText(decodeEntities(label), getConfig()), {
useHtmlLabels,
width: node.width || getConfig().flowchart.wrappingWidth,
cssClasses: 'markdown-node-label',