mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-10-03 14:19:38 +02:00
Merge branch 'develop' into sidv/zenuml
* develop: (22 commits) Update docs Added CKEditor and GitHub Writer to available integrations. chore(deps): update all patch dependencies build(deps): fix broken pnpm-lock.yaml file Mermaid version 10.2.0 Mermaid Version 10.2.0-rc.4 Label background fix Test commit Fix for regression error in sequenceDiagrams Fix visibility issue for fields fix parsing issue with class diagrams fix: Use unicode arrows in quadrant chart axis fix: Use unicode arrows in quadrant chart axis fix lint command Bump version Back to JS with jsdoc types Add unsupported text Reduce changes in test Fix deps Fix lockfile ...
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mermaid",
|
||||
"version": "10.2.0-rc.2",
|
||||
"version": "10.2.0",
|
||||
"description": "Markdown-ish syntax for generating flowcharts, sequence diagrams, class diagrams, gantt charts and git graphs.",
|
||||
"type": "module",
|
||||
"module": "./dist/mermaid.core.mjs",
|
||||
@@ -53,7 +53,6 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
"@khanacademy/simple-markdown": "^0.9.0",
|
||||
"cytoscape": "^3.23.0",
|
||||
"cytoscape-cose-bilkent": "^4.1.0",
|
||||
"cytoscape-fcose": "^2.1.0",
|
||||
@@ -64,6 +63,7 @@
|
||||
"elkjs": "^0.8.2",
|
||||
"khroma": "^2.0.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mdast-util-from-markdown": "^1.3.0",
|
||||
"non-layered-tidy-tree-layout": "^2.0.2",
|
||||
"stylis": "^4.1.3",
|
||||
"ts-dedent": "^2.2.0",
|
||||
|
@@ -361,72 +361,6 @@ export const drawNote = function (elem, note, conf, diagObj) {
|
||||
};
|
||||
|
||||
export const parseMember = function (text) {
|
||||
// Note: these two regular expressions don't parse the official UML syntax for attributes
|
||||
// and methods. They parse a Java-style syntax of the form
|
||||
// "String name" (for attributes) and "String name(int x)" for methods
|
||||
const fieldRegEx = /^([#+~-])?(\w+)(~\w+~|\[])?\s+(\w+) *([$*])?$/;
|
||||
const methodRegEx = /^([#+|~-])?(\w+) *\( *(.*)\) *([$*])? *(\w*[[\]|~]*\s*\w*~?)$/;
|
||||
|
||||
let fieldMatch = text.match(fieldRegEx);
|
||||
let methodMatch = text.match(methodRegEx);
|
||||
|
||||
if (fieldMatch && !methodMatch) {
|
||||
return buildFieldDisplay(fieldMatch);
|
||||
} else if (methodMatch) {
|
||||
return buildMethodDisplay(methodMatch);
|
||||
} else {
|
||||
return buildLegacyDisplay(text);
|
||||
}
|
||||
};
|
||||
|
||||
const buildFieldDisplay = function (parsedText) {
|
||||
let cssStyle = '';
|
||||
let displayText = '';
|
||||
|
||||
try {
|
||||
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
||||
let fieldType = parsedText[2] ? parsedText[2].trim() : '';
|
||||
let genericType = parsedText[3] ? parseGenericTypes(parsedText[3].trim()) : '';
|
||||
let fieldName = parsedText[4] ? parsedText[4].trim() : '';
|
||||
let classifier = parsedText[5] ? parsedText[5].trim() : '';
|
||||
|
||||
displayText = visibility + fieldType + genericType + ' ' + fieldName;
|
||||
cssStyle = parseClassifier(classifier);
|
||||
} catch (err) {
|
||||
displayText = parsedText;
|
||||
}
|
||||
|
||||
return {
|
||||
displayText: displayText,
|
||||
cssStyle: cssStyle,
|
||||
};
|
||||
};
|
||||
|
||||
const buildMethodDisplay = function (parsedText) {
|
||||
let cssStyle = '';
|
||||
let displayText = '';
|
||||
|
||||
try {
|
||||
let visibility = parsedText[1] ? parsedText[1].trim() : '';
|
||||
let methodName = parsedText[2] ? parsedText[2].trim() : '';
|
||||
let parameters = parsedText[3] ? parseGenericTypes(parsedText[3].trim()) : '';
|
||||
let classifier = parsedText[4] ? parsedText[4].trim() : '';
|
||||
let returnType = parsedText[5] ? ' : ' + parseGenericTypes(parsedText[5]).trim() : '';
|
||||
|
||||
displayText = visibility + methodName + '(' + parameters + ')' + returnType;
|
||||
cssStyle = parseClassifier(classifier);
|
||||
} catch (err) {
|
||||
displayText = parsedText;
|
||||
}
|
||||
|
||||
return {
|
||||
displayText: displayText,
|
||||
cssStyle: cssStyle,
|
||||
};
|
||||
};
|
||||
|
||||
const buildLegacyDisplay = function (text) {
|
||||
// if for some reason we don't have any match, use old format to parse text
|
||||
let displayText = '';
|
||||
let cssStyle = '';
|
||||
let returnType = '';
|
||||
@@ -444,14 +378,15 @@ const buildLegacyDisplay = function (text) {
|
||||
cssStyle = parseClassifier(lastChar);
|
||||
}
|
||||
|
||||
let startIndex = visibility === '' ? 0 : 1;
|
||||
const startIndex = visibility === '' ? 0 : 1;
|
||||
let endIndex = cssStyle === '' ? text.length : text.length - 1;
|
||||
text = text.substring(startIndex, endIndex);
|
||||
|
||||
let methodStart = text.indexOf('(');
|
||||
let methodEnd = text.indexOf(')');
|
||||
const methodStart = text.indexOf('(');
|
||||
const methodEnd = text.indexOf(')');
|
||||
const isMethod = methodStart > 1 && methodEnd > methodStart && methodEnd <= text.length;
|
||||
|
||||
if (methodStart > 1 && methodEnd > methodStart && methodEnd <= text.length) {
|
||||
if (isMethod) {
|
||||
let methodName = text.substring(0, methodStart).trim();
|
||||
|
||||
const parameters = text.substring(methodStart + 1, methodEnd);
|
||||
@@ -478,7 +413,7 @@ const buildLegacyDisplay = function (text) {
|
||||
}
|
||||
} else {
|
||||
// finally - if all else fails, just send the text back as written (other than parsing for generic types)
|
||||
displayText = parseGenericTypes(text);
|
||||
displayText = visibility + parseGenericTypes(text);
|
||||
}
|
||||
|
||||
return {
|
||||
|
@@ -1,3 +1,6 @@
|
||||
// import khroma from 'khroma';
|
||||
import * as khroma from 'khroma';
|
||||
|
||||
/** Returns the styles given options */
|
||||
export interface FlowChartStyleOptions {
|
||||
arrowheadColor: string;
|
||||
@@ -15,6 +18,18 @@ export interface FlowChartStyleOptions {
|
||||
titleColor: string;
|
||||
}
|
||||
|
||||
const fade = (color: string, opacity: number) => {
|
||||
// @ts-ignore TODO: incorrect types from khroma
|
||||
const channel = khroma.channel;
|
||||
|
||||
const r = channel(color, 'r');
|
||||
const g = channel(color, 'g');
|
||||
const b = channel(color, 'b');
|
||||
|
||||
// @ts-ignore incorrect types from khroma
|
||||
return khroma.rgba(r, g, b, opacity);
|
||||
};
|
||||
|
||||
const getStyles = (options: FlowChartStyleOptions) =>
|
||||
`.label {
|
||||
font-family: ${options.fontFamily};
|
||||
@@ -82,6 +97,12 @@ const getStyles = (options: FlowChartStyleOptions) =>
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* For html labels only */
|
||||
.labelBkg {
|
||||
background-color: ${fade(options.edgeLabelBackground, 0.5)};
|
||||
// background-color:
|
||||
}
|
||||
|
||||
.cluster rect {
|
||||
fill: ${options.clusterBkg};
|
||||
stroke: ${options.clusterBorder};
|
||||
|
@@ -119,10 +119,10 @@ points
|
||||
|
||||
axisDetails
|
||||
: X-AXIS text AXIS-TEXT-DELIMITER text {yy.setXAxisLeftText($2); yy.setXAxisRightText($4);}
|
||||
| X-AXIS text AXIS-TEXT-DELIMITER {$2.text += $3; yy.setXAxisLeftText($2);}
|
||||
| X-AXIS text AXIS-TEXT-DELIMITER {$2.text += " ⟶ "; yy.setXAxisLeftText($2);}
|
||||
| X-AXIS text {yy.setXAxisLeftText($2);}
|
||||
| Y-AXIS text AXIS-TEXT-DELIMITER text {yy.setYAxisBottomText($2); yy.setYAxisTopText($4);}
|
||||
| Y-AXIS text AXIS-TEXT-DELIMITER {$2.text += $3; yy.setYAxisBottomText($2);}
|
||||
| Y-AXIS text AXIS-TEXT-DELIMITER {$2.text += " ⟶ "; yy.setYAxisBottomText($2);}
|
||||
| Y-AXIS text {yy.setYAxisBottomText($2);}
|
||||
;
|
||||
|
||||
|
@@ -93,7 +93,7 @@ describe('Testing quadrantChart jison file', () => {
|
||||
str = 'quadrantChart\n x-AxIs "Urgent(* +=[❤" --> ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setXAxisLeftText).toHaveBeenCalledWith({
|
||||
text: 'Urgent(* +=[❤ --> ',
|
||||
text: 'Urgent(* +=[❤ ⟶ ',
|
||||
type: 'text',
|
||||
});
|
||||
expect(mockDB.setXAxisRightText).not.toHaveBeenCalled();
|
||||
@@ -131,7 +131,7 @@ describe('Testing quadrantChart jison file', () => {
|
||||
str = 'quadrantChart\n y-AxIs "Urgent(* +=[❤" --> ';
|
||||
expect(parserFnConstructor(str)).not.toThrow();
|
||||
expect(mockDB.setYAxisBottomText).toHaveBeenCalledWith({
|
||||
text: 'Urgent(* +=[❤ --> ',
|
||||
text: 'Urgent(* +=[❤ ⟶ ',
|
||||
type: 'text',
|
||||
});
|
||||
expect(mockDB.setYAxisTopText).not.toHaveBeenCalled();
|
||||
|
@@ -578,7 +578,7 @@ export const drawLoop = function (elem, loopModel, labelText, conf) {
|
||||
txt.class = 'labelText';
|
||||
|
||||
drawLabel(g, txt);
|
||||
txt = svgDrawCommon.getTextObj();
|
||||
txt = getTextObj();
|
||||
txt.text = loopModel.title;
|
||||
txt.x = loopModel.startx + labelBoxWidth / 2 + (loopModel.stopx - loopModel.startx) / 2;
|
||||
txt.y = loopModel.starty + boxMargin + boxTextMargin;
|
||||
@@ -764,6 +764,37 @@ export const insertArrowCrossHead = function (elem) {
|
||||
// this is actual shape for arrowhead
|
||||
};
|
||||
|
||||
export const getTextObj = function () {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
fill: undefined,
|
||||
anchor: undefined,
|
||||
style: '#666',
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
textMargin: 0,
|
||||
rx: 0,
|
||||
ry: 0,
|
||||
tspan: true,
|
||||
valign: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
export const getNoteRect = function () {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
fill: '#EDF2AE',
|
||||
stroke: '#666',
|
||||
width: 100,
|
||||
anchor: 'start',
|
||||
height: 100,
|
||||
rx: 0,
|
||||
ry: 0,
|
||||
};
|
||||
};
|
||||
|
||||
const _drawTextCandidateFunc = (function () {
|
||||
/**
|
||||
* @param {any} content
|
||||
@@ -1004,6 +1035,8 @@ export default {
|
||||
insertDatabaseIcon,
|
||||
insertComputerIcon,
|
||||
insertClockIcon,
|
||||
getTextObj,
|
||||
getNoteRect,
|
||||
popupMenu,
|
||||
popdownMenu,
|
||||
fixLifeLineHeights,
|
||||
|
@@ -10,6 +10,7 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
- [Using code blocks](https://github.blog/2022-02-14-include-diagrams-markdown-files-mermaid/) (**Native support**)
|
||||
- [GitHub action: Compile mermaid to image](https://github.com/neenjaw/compile-mermaid-markdown-action)
|
||||
- [svg-generator](https://github.com/SimonKenyonShepard/mermaidjs-github-svg-generator)
|
||||
- [GitHub Writer](https://github.com/ckeditor/github-writer)
|
||||
- [GitLab](https://docs.gitlab.com/ee/user/markdown.html#diagrams-and-flowcharts) (**Native support**)
|
||||
- [Gitea](https://gitea.io) (**Native support**)
|
||||
- [Azure Devops](https://docs.microsoft.com/en-us/azure/devops/project/wiki/wiki-markdown-guidance?view=azure-devops#add-mermaid-diagrams-to-a-wiki-page) (**Native support**)
|
||||
@@ -136,6 +137,8 @@ They also serve as proof of concept, for the variety of things that can be built
|
||||
- [Named block =Diagram](https://github.com/zag/podlite/tree/main/packages/podlite-diagrams)
|
||||
- [GNU Nano](https://www.nano-editor.org/)
|
||||
- [Nano Mermaid](https://github.com/Yash-Singh1/nano-mermaid)
|
||||
- [CKEditor](https://github.com/ckeditor/ckeditor5)
|
||||
- [CKEditor 5 Mermaid plugin](https://github.com/ckeditor/ckeditor5-mermaid)
|
||||
|
||||
## Document Generation
|
||||
|
||||
|
@@ -30,7 +30,7 @@
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"vite": "^4.3.3",
|
||||
"vite-plugin-pwa": "^0.15.0",
|
||||
"vitepress": "1.0.0-alpha.76",
|
||||
"vitepress": "1.0.0-beta.1",
|
||||
"workbox-window": "^6.5.4"
|
||||
}
|
||||
}
|
||||
|
@@ -39,7 +39,30 @@ flowchart LR
|
||||
id1[This is the text in the box]
|
||||
```
|
||||
|
||||
## Graph
|
||||
#### Unicode text
|
||||
|
||||
Use `"` to enclose the unicode text.
|
||||
|
||||
```mermaid-example
|
||||
flowchart LR
|
||||
id["This ❤ Unicode"]
|
||||
```
|
||||
|
||||
#### Markdown formatting
|
||||
|
||||
Use double quotes and backticks "\` text \`" to enclose the markdown text.
|
||||
|
||||
```mermaid-example
|
||||
%%{init: {"flowchart": {"htmlLabels": false}} }%%
|
||||
flowchart LR
|
||||
markdown["`This **is** _Markdown_`"]
|
||||
newLines["`Line1
|
||||
Line 2
|
||||
Line 3`"]
|
||||
markdown --> newLines
|
||||
```
|
||||
|
||||
### Direction
|
||||
|
||||
This statement declares the direction of the Flowchart.
|
||||
|
||||
@@ -57,15 +80,13 @@ flowchart LR
|
||||
Start --> Stop
|
||||
```
|
||||
|
||||
## Flowchart Orientation
|
||||
|
||||
Possible FlowChart orientations are:
|
||||
|
||||
- TB - top to bottom
|
||||
- TD - top-down/ same as top to bottom
|
||||
- BT - bottom to top
|
||||
- RL - right to left
|
||||
- LR - left to right
|
||||
- TB - Top to bottom
|
||||
- TD - Top-down/ same as top to bottom
|
||||
- BT - Bottom to top
|
||||
- RL - Right to left
|
||||
- LR - Left to right
|
||||
|
||||
## Node shapes
|
||||
|
||||
|
@@ -154,13 +154,7 @@ export const encodeEntities = function (text: string): string {
|
||||
* @returns
|
||||
*/
|
||||
export const decodeEntities = function (text: string): string {
|
||||
let txt = text;
|
||||
|
||||
txt = txt.replace(/fl°°/g, '&#');
|
||||
txt = txt.replace(/fl°/g, '&');
|
||||
txt = txt.replace(/¶ß/g, ';');
|
||||
|
||||
return txt;
|
||||
return text.replace(/fl°°/g, '&#').replace(/fl°/g, '&').replace(/¶ß/g, ';');
|
||||
};
|
||||
|
||||
// append !important; to each cssClass followed by a final !important, all enclosed in { }
|
||||
|
@@ -1,7 +1,4 @@
|
||||
import { select } from 'd3';
|
||||
import { log } from '../logger.js';
|
||||
import { getConfig } from '../config.js';
|
||||
import { evaluate } from '../diagrams/common/common.js';
|
||||
import { decodeEntities } from '../mermaidAPI.js';
|
||||
import { markdownToHTML, markdownToLines } from '../rendering-util/handle-markdown-text.js';
|
||||
/**
|
||||
@@ -19,9 +16,10 @@ function applyStyle(dom, styleFn) {
|
||||
* @param {any} node
|
||||
* @param width
|
||||
* @param classes
|
||||
* @param addBackground
|
||||
* @returns {SVGForeignObjectElement} Node
|
||||
*/
|
||||
function addHtmlSpan(element, node, width, classes) {
|
||||
function addHtmlSpan(element, node, width, classes, addBackground = false) {
|
||||
const fo = element.append('foreignObject');
|
||||
// const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
|
||||
// const newEl = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
|
||||
@@ -32,7 +30,8 @@ function addHtmlSpan(element, node, width, classes) {
|
||||
const label = node.label;
|
||||
const labelClass = node.isNode ? 'nodeLabel' : 'edgeLabel';
|
||||
div.html(
|
||||
`<span class="${labelClass} ${classes}" ` +
|
||||
`
|
||||
<span class="${labelClass} ${classes}" ` +
|
||||
(node.labelStyle ? 'style="' + node.labelStyle + '"' : '') +
|
||||
'>' +
|
||||
label +
|
||||
@@ -44,6 +43,9 @@ function addHtmlSpan(element, node, width, classes) {
|
||||
div.style('white-space', 'nowrap');
|
||||
div.style('max-width', width + 'px');
|
||||
div.attr('xmlns', 'http://www.w3.org/1999/xhtml');
|
||||
if (addBackground) {
|
||||
div.attr('class', 'labelBkg');
|
||||
}
|
||||
|
||||
let bbox = div.node().getBoundingClientRect();
|
||||
if (bbox.width === width) {
|
||||
@@ -203,21 +205,10 @@ export const createText = (
|
||||
),
|
||||
labelStyle: style.replace('fill:', 'color:'),
|
||||
};
|
||||
let vertexNode = addHtmlSpan(el, node, width, classes);
|
||||
let vertexNode = addHtmlSpan(el, node, width, classes, addSvgBackground);
|
||||
return vertexNode;
|
||||
} else {
|
||||
const structuredText = markdownToLines(text);
|
||||
const special = ['"', "'", '.', ',', ':', ';', '!', '?', '(', ')', '[', ']', '{', '}'];
|
||||
let lastWord;
|
||||
structuredText.forEach((line) => {
|
||||
line.forEach((word) => {
|
||||
if (special.includes(word.content) && lastWord) {
|
||||
lastWord.content += word.content;
|
||||
word.content = '';
|
||||
}
|
||||
lastWord = word;
|
||||
});
|
||||
});
|
||||
const svgLabel = createFormattedText(width, el, structuredText, addSvgBackground);
|
||||
return svgLabel;
|
||||
}
|
||||
|
@@ -1,61 +1,55 @@
|
||||
import SimpleMarkdown from '@khanacademy/simple-markdown';
|
||||
import { fromMarkdown } from 'mdast-util-from-markdown';
|
||||
import { dedent } from 'ts-dedent';
|
||||
|
||||
/**
|
||||
*
|
||||
* @param markdown
|
||||
* @param {string} markdown markdown to process
|
||||
* @returns {string} processed markdown
|
||||
*/
|
||||
function preprocessMarkdown(markdown) {
|
||||
// Replace multiple newlines with a single newline
|
||||
const withoutMultipleNewlines = markdown.replace(/\n{2,}/g, '\n');
|
||||
// Remove extra spaces at the beginning of each line
|
||||
const withoutExtraSpaces = withoutMultipleNewlines.replace(/^\s+/gm, '');
|
||||
const withoutExtraSpaces = dedent(withoutMultipleNewlines);
|
||||
return withoutExtraSpaces;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param markdown
|
||||
* @param {string} markdown markdown to split into lines
|
||||
*/
|
||||
export function markdownToLines(markdown) {
|
||||
const preprocessedMarkdown = preprocessMarkdown(markdown);
|
||||
const mdParse = SimpleMarkdown.defaultBlockParse;
|
||||
const syntaxTree = mdParse(preprocessedMarkdown);
|
||||
|
||||
let lines = [[]];
|
||||
const { children } = fromMarkdown(preprocessedMarkdown);
|
||||
const lines = [[]];
|
||||
let currentLine = 0;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @param parentType
|
||||
* @param {import('mdast').Content} node
|
||||
* @param {string} [parentType]
|
||||
*/
|
||||
function processNode(node, parentType) {
|
||||
function processNode(node, parentType = 'normal') {
|
||||
if (node.type === 'text') {
|
||||
const textLines = node.content.split('\n');
|
||||
|
||||
const textLines = node.value.split('\n');
|
||||
textLines.forEach((textLine, index) => {
|
||||
if (index !== 0) {
|
||||
currentLine++;
|
||||
lines.push([]);
|
||||
}
|
||||
|
||||
// textLine.split(/ (?=[^!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~]+)/).forEach((word) => {
|
||||
textLine.split(' ').forEach((word) => {
|
||||
if (word) {
|
||||
lines[currentLine].push({ content: word, type: parentType || 'normal' });
|
||||
lines[currentLine].push({ content: word, type: parentType });
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (node.type === 'strong' || node.type === 'em') {
|
||||
node.content.forEach((contentNode) => {
|
||||
} else if (node.type === 'strong' || node.type === 'emphasis') {
|
||||
node.children.forEach((contentNode) => {
|
||||
processNode(contentNode, node.type);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
syntaxTree.forEach((treeNode) => {
|
||||
children.forEach((treeNode) => {
|
||||
if (treeNode.type === 'paragraph') {
|
||||
treeNode.content.forEach((contentNode) => {
|
||||
treeNode.children.forEach((contentNode) => {
|
||||
processNode(contentNode);
|
||||
});
|
||||
}
|
||||
@@ -65,30 +59,27 @@ export function markdownToLines(markdown) {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param markdown
|
||||
* @param {string} markdown markdown to convert to HTML
|
||||
* @returns {string} HTML
|
||||
*/
|
||||
export function markdownToHTML(markdown) {
|
||||
const mdParse = SimpleMarkdown.defaultBlockParse;
|
||||
const syntaxTree = mdParse(markdown);
|
||||
const { children } = fromMarkdown(markdown);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param node
|
||||
* @param {import('mdast').Content} node
|
||||
*/
|
||||
function output(node) {
|
||||
if (node.type === 'text') {
|
||||
return node.content.replace(/\n/g, '<br/>');
|
||||
return node.value.replace(/\n/g, '<br/>');
|
||||
} else if (node.type === 'strong') {
|
||||
return `<strong>${node.content.map(output).join('')}</strong>`;
|
||||
} else if (node.type === 'em') {
|
||||
return `<em>${node.content.map(output).join('')}</em>`;
|
||||
return `<strong>${node.children.map(output).join('')}</strong>`;
|
||||
} else if (node.type === 'emphasis') {
|
||||
return `<em>${node.children.map(output).join('')}</em>`;
|
||||
} else if (node.type === 'paragraph') {
|
||||
return `<p>${node.content.map(output).join('')}</p>`;
|
||||
} else {
|
||||
return '';
|
||||
return `<p>${node.children.map(output).join('')}</p>`;
|
||||
}
|
||||
return `Unsupported markdown: ${node.type}`;
|
||||
}
|
||||
|
||||
return syntaxTree.map(output).join('');
|
||||
return children.map(output).join('');
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
// import { test } from 'vitest';
|
||||
import { markdownToLines, markdownToHTML } from './handle-markdown-text';
|
||||
import { test } from 'vitest';
|
||||
import { markdownToLines, markdownToHTML } from './handle-markdown-text.js';
|
||||
import { test, expect } from 'vitest';
|
||||
|
||||
test('markdownToLines - Basic test', () => {
|
||||
const input = `This is regular text
|
||||
@@ -37,9 +36,9 @@ Here is a line *with an italic* section`;
|
||||
{ content: 'is', type: 'normal' },
|
||||
{ content: 'a', type: 'normal' },
|
||||
{ content: 'line', type: 'normal' },
|
||||
{ content: 'with', type: 'em' },
|
||||
{ content: 'an', type: 'em' },
|
||||
{ content: 'italic', type: 'em' },
|
||||
{ content: 'with', type: 'emphasis' },
|
||||
{ content: 'an', type: 'emphasis' },
|
||||
{ content: 'italic', type: 'emphasis' },
|
||||
{ content: 'section', type: 'normal' },
|
||||
],
|
||||
];
|
||||
@@ -117,7 +116,6 @@ test('markdownToLines - paragraph 1', () => {
|
||||
|
||||
test('markdownToLines - paragraph', () => {
|
||||
const input = `**Start** with
|
||||
|
||||
a second line`;
|
||||
|
||||
const expectedOutput = [
|
||||
@@ -144,7 +142,7 @@ test('markdownToLines - Only italic formatting', () => {
|
||||
{ content: 'This', type: 'normal' },
|
||||
{ content: 'is', type: 'normal' },
|
||||
{ content: 'an', type: 'normal' },
|
||||
{ content: 'italic', type: 'em' },
|
||||
{ content: 'italic', type: 'emphasis' },
|
||||
{ content: 'test', type: 'normal' },
|
||||
],
|
||||
];
|
||||
@@ -158,7 +156,7 @@ it('markdownToLines - Mixed formatting', () => {
|
||||
|
||||
const expectedOutput = [
|
||||
[
|
||||
{ content: 'Italic', type: 'em' },
|
||||
{ content: 'Italic', type: 'emphasis' },
|
||||
{ content: 'and', type: 'normal' },
|
||||
{ content: 'bold', type: 'strong' },
|
||||
{ content: 'formatting', type: 'normal' },
|
||||
@@ -179,21 +177,15 @@ Word!`;
|
||||
{ content: 'dog', type: 'normal' },
|
||||
{ content: 'in', type: 'normal' },
|
||||
{ content: 'the', type: 'strong' },
|
||||
{ content: 'hog', type: 'normal' },
|
||||
{ content: '.', type: 'normal' },
|
||||
{ content: '.', type: 'normal' },
|
||||
{ content: '.', type: 'normal' },
|
||||
{ content: 'hog...', type: 'normal' },
|
||||
{ content: 'a', type: 'normal' },
|
||||
{ content: 'very', type: 'em' },
|
||||
{ content: 'long', type: 'em' },
|
||||
{ content: 'text', type: 'em' },
|
||||
{ content: 'very', type: 'emphasis' },
|
||||
{ content: 'long', type: 'emphasis' },
|
||||
{ content: 'text', type: 'emphasis' },
|
||||
{ content: 'about', type: 'normal' },
|
||||
{ content: 'it', type: 'normal' },
|
||||
],
|
||||
[
|
||||
{ content: 'Word', type: 'normal' },
|
||||
{ content: '!', type: 'normal' },
|
||||
],
|
||||
[{ content: 'Word!', type: 'normal' }],
|
||||
];
|
||||
|
||||
const output = markdownToLines(input);
|
||||
@@ -246,8 +238,16 @@ test('markdownToHTML - Only italic formatting', () => {
|
||||
|
||||
test('markdownToHTML - Mixed formatting', () => {
|
||||
const input = `*Italic* and **bold** formatting`;
|
||||
|
||||
const expectedOutput = `<p><em>Italic</em> and <strong>bold</strong> formatting</p>`;
|
||||
const output = markdownToHTML(input);
|
||||
expect(output).toEqual(expectedOutput);
|
||||
});
|
||||
|
||||
test('markdownToHTML - Unsupported formatting', () => {
|
||||
expect(
|
||||
markdownToHTML(`Hello
|
||||
- l1
|
||||
- l2
|
||||
- l3`)
|
||||
).toMatchInlineSnapshot('"<p>Hello</p>Unsupported markdown: list"');
|
||||
});
|
@@ -1,4 +1,4 @@
|
||||
import { darken, lighten, adjust, invert, isDark } from 'khroma';
|
||||
import { darken, lighten, adjust, invert, isDark, toRgba } from 'khroma';
|
||||
import { mkBorder } from './theme-helpers.js';
|
||||
import {
|
||||
oldAttributeBackgroundColorEven,
|
||||
|
Reference in New Issue
Block a user