@liveblocks/react-ui
Version:
A set of React pre-built components for the Liveblocks products. Liveblocks is the all-in-one toolkit to build collaborative products like Figma, Notion, and more.
665 lines (661 loc) • 25 kB
JavaScript
;
var jsxRuntime = require('react/jsx-runtime');
var core = require('@liveblocks/core');
var reactSlot = require('@radix-ui/react-slot');
var marked = require('marked');
var react = require('react');
const LIST_ITEM_CHECKBOX_REGEX = /^\[\s?(x)?\]?$/i;
const PARTIAL_LINK_IMAGE_REGEX = /(?<!\\)(?<image>!)?\[(?!\^)(?<text>[^\]]*)(?:\](?:\((?<url>[^)]*)?)?)?$/;
const PARTIAL_TABLE_HEADER_REGEX = /^\s*\|(?:[^|\n]+(?:\|[^|\n]+)*?)?\|?\s*(?:\n\s*\|\s*[-:|\s]*\s*)?$/;
const PARTIAL_EMOJI_REGEX = /(?:\u200D|\uFE0F|\u20E3|\p{Regional_Indicator}|\p{Emoji_Presentation}|\p{Emoji_Modifier_Base}|\p{Emoji_Modifier})+$/u;
const TRAILING_NON_WHITESPACE_REGEX = /^\S*/;
const WHITESPACE_REGEX = /\s/;
const NEWLINE_REGEX = /\r\n?/g;
const BUFFERED_CHARACTERS_REGEX = /(?<!\\)((\*+|_+|~+|`+|\++|-{0,2}|={0,2}|\\|!|<\/?)\s*)$/;
const SINGLE_CHARACTER_REGEX = /^\s*(\S\s*)$/;
const LEFT_ANGLE_BRACKET_REGEX = /</g;
const RIGHT_ANGLE_BRACKET_REGEX = />/g;
const AMPERSAND_REGEX = /&(?!#?[0-9A-Za-z]+;)/g;
const DEFAULT_PARTIAL_LINK_URL = "#";
const defaultComponents = {
Paragraph: ({ children }) => {
return /* @__PURE__ */ jsxRuntime.jsx("p", { children });
},
Inline: ({ type, children }) => {
switch (type) {
case "strong":
return /* @__PURE__ */ jsxRuntime.jsx("strong", { children });
case "em":
return /* @__PURE__ */ jsxRuntime.jsx("em", { children });
case "code":
return /* @__PURE__ */ jsxRuntime.jsx("code", { children });
case "del":
return /* @__PURE__ */ jsxRuntime.jsx("del", { children });
default:
core.assertNever(type, "Unknown inline type");
}
},
CodeBlock: ({ language, code }) => {
return /* @__PURE__ */ jsxRuntime.jsx("pre", { "data-language": language ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsx("code", { children: code }) });
},
Link: ({ href, title, children }) => {
return /* @__PURE__ */ jsxRuntime.jsx("a", { href, title, target: "_blank", rel: "noopener noreferrer", children });
},
Heading: ({ level, children }) => {
const Heading = `h${level}`;
return /* @__PURE__ */ jsxRuntime.jsx(Heading, { children });
},
Image: ({ src, alt, title }) => {
return /* @__PURE__ */ jsxRuntime.jsx("img", { src, alt, title });
},
Blockquote: ({ children }) => {
return /* @__PURE__ */ jsxRuntime.jsx("blockquote", { children });
},
Table: ({ headings, rows }) => {
return /* @__PURE__ */ jsxRuntime.jsxs("table", { children: [
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsx("tr", { children: headings.map((heading, index) => {
return /* @__PURE__ */ jsxRuntime.jsx("th", { align: heading.align, children: heading.children }, index);
}) }) }),
/* @__PURE__ */ jsxRuntime.jsx("tbody", { children: rows.map((row, index) => {
return /* @__PURE__ */ jsxRuntime.jsx("tr", { children: row.map((cell, index2) => {
return /* @__PURE__ */ jsxRuntime.jsx("td", { align: cell.align, children: cell.children }, index2);
}) }, index);
}) })
] });
},
List: ({ type, items, start }) => {
const List = type === "ordered" ? "ol" : "ul";
return /* @__PURE__ */ jsxRuntime.jsx(List, { start: start === 1 ? void 0 : start, children: items.map((item, index) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: item.children }, index)) });
},
Separator: () => {
return /* @__PURE__ */ jsxRuntime.jsx("hr", {});
}
};
const Markdown = react.forwardRef(
({ content, partial, components, asChild, ...props }, forwardedRef) => {
const Component = asChild ? reactSlot.Slot : "div";
const tokens = react.useMemo(() => {
if (!content) {
return [];
}
return partial ? tokenizePartial(content) : tokenize(content);
}, [content, partial]);
return /* @__PURE__ */ jsxRuntime.jsx(Component, { ...props, ref: forwardedRef, children: tokens.map((token, index) => {
return /* @__PURE__ */ jsxRuntime.jsx(
MemoizedMarkdownToken,
{
token,
components,
partial
},
index
);
}) });
}
);
const MemoizedMarkdownToken = react.memo(
({
token,
components
}) => {
return /* @__PURE__ */ jsxRuntime.jsx(MarkdownToken, { token, components });
},
(previousProps, nextProps) => {
const previousToken = previousProps.token;
const nextToken = nextProps.token;
if (previousToken.type !== nextToken.type || previousProps.partial !== nextProps.partial) {
return false;
}
let previousContent = previousToken.raw;
let nextContent = nextToken.raw;
if ("text" in previousToken && "text" in nextToken) {
previousContent = previousToken.text;
nextContent = nextToken.text;
}
if (previousContent.length !== nextContent.length) {
return false;
}
return previousContent === nextContent;
}
);
function MarkdownToken({
token,
components
}) {
switch (token.type) {
case "escape": {
return token.text;
}
case "space": {
return null;
}
case "text": {
if (token.tokens !== void 0) {
return /* @__PURE__ */ jsxRuntime.jsx(MarkdownTokens, { tokens: token.tokens, components });
} else {
return parseHtmlEntities(token.text);
}
}
case "br": {
return /* @__PURE__ */ jsxRuntime.jsx("br", {});
}
case "paragraph": {
const Paragraph = components?.Paragraph ?? defaultComponents.Paragraph;
return /* @__PURE__ */ jsxRuntime.jsx(Paragraph, { children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownTokens, { tokens: token.tokens, components }) });
}
case "heading": {
const Heading = components?.Heading ?? defaultComponents.Heading;
return /* @__PURE__ */ jsxRuntime.jsx(Heading, { level: clampHeadingLevel(token.depth), children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownTokens, { tokens: token.tokens, components }) });
}
case "strong": {
const Inline = components?.Inline ?? defaultComponents.Inline;
return /* @__PURE__ */ jsxRuntime.jsx(Inline, { type: "strong", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownTokens, { tokens: token.tokens, components }) });
}
case "em": {
const Inline = components?.Inline ?? defaultComponents.Inline;
return /* @__PURE__ */ jsxRuntime.jsx(Inline, { type: "em", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownTokens, { tokens: token.tokens, components }) });
}
case "codespan": {
const Inline = components?.Inline ?? defaultComponents.Inline;
return /* @__PURE__ */ jsxRuntime.jsx(Inline, { type: "code", children: parseHtmlEntities(token.text) });
}
case "del": {
const Inline = components?.Inline ?? defaultComponents.Inline;
return /* @__PURE__ */ jsxRuntime.jsx(Inline, { type: "del", children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownTokens, { tokens: token.tokens, components }) });
}
case "link": {
const href = core.sanitizeUrl(token.href);
if (href === null) {
return /* @__PURE__ */ jsxRuntime.jsx(MarkdownTokens, { tokens: token.tokens, components });
}
const Link = components?.Link ?? defaultComponents.Link;
return /* @__PURE__ */ jsxRuntime.jsx(Link, { href, title: token.title ?? void 0, children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownTokens, { tokens: token.tokens, components }) });
}
case "code": {
let language = void 0;
if (token.lang !== void 0) {
language = token.lang.match(TRAILING_NON_WHITESPACE_REGEX)?.[0] ?? void 0;
}
const CodeBlock = components?.CodeBlock ?? defaultComponents.CodeBlock;
return /* @__PURE__ */ jsxRuntime.jsx(CodeBlock, { language, code: token.text || " " });
}
case "blockquote": {
const Blockquote = components?.Blockquote ?? defaultComponents.Blockquote;
return /* @__PURE__ */ jsxRuntime.jsx(Blockquote, { children: /* @__PURE__ */ jsxRuntime.jsx(
MarkdownTokens,
{
tokens: token.tokens,
components,
normalizeToBlockTokens: true
}
) });
}
case "list": {
const List = components?.List ?? defaultComponents.List;
const items = token.items.map((item) => {
let tokens = item.tokens;
if (item.task) {
tokens = [
{
type: "checkbox",
checked: Boolean(item.checked),
raw: `[${item.checked ? "x" : " "}]`
},
...tokens
];
}
return {
checked: item.task ? item.checked : void 0,
children: /* @__PURE__ */ jsxRuntime.jsx(
MarkdownTokens,
{
tokens,
components,
normalizeToBlockTokens: (
// A non-loose list item doesn't need to be wrapped in block tokens.
item.tokens.length > 0 ? item.loose : false
)
}
)
};
});
const props = token.ordered ? { type: "ordered", items, start: token.start || 1 } : { type: "unordered", items };
return /* @__PURE__ */ jsxRuntime.jsx(List, { ...props });
}
case "checkbox": {
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
/* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", disabled: true, checked: token.checked }),
" "
] });
}
case "table": {
const Table = components?.Table ?? defaultComponents.Table;
const headings = token.header.map(
(cell) => ({
align: cell.align ?? void 0,
children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownTokens, { tokens: cell.tokens, components })
})
);
const rows = token.rows.map(
(row) => row.map((cell) => ({
align: cell.align ?? void 0,
children: /* @__PURE__ */ jsxRuntime.jsx(MarkdownTokens, { tokens: cell.tokens, components })
}))
);
return /* @__PURE__ */ jsxRuntime.jsx(Table, { headings, rows });
}
case "image": {
const href = core.sanitizeUrl(token.href);
if (href === null) {
return token.text;
}
const Image = components?.Image ?? defaultComponents.Image;
return /* @__PURE__ */ jsxRuntime.jsx(Image, { src: href, alt: token.text, title: token.title ?? void 0 });
}
case "hr": {
const Separator = components?.Separator ?? defaultComponents.Separator;
return /* @__PURE__ */ jsxRuntime.jsx(Separator, {});
}
case "html": {
return parseHtmlEntities(token.text);
}
default: {
return null;
}
}
}
function MarkdownTokens({
tokens,
components,
normalizeToBlockTokens = false
}) {
assertTokens(tokens);
let normalizedTokens = [];
if (normalizeToBlockTokens) {
let leadingCheckboxToken = tokens[0]?.type === "checkbox" ? tokens[0] : null;
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i];
switch (token.type) {
case "text": {
const paragraphTextTokens = [token];
while (i + 1 < tokens.length && tokens[i + 1].type === "text") {
i++;
paragraphTextTokens.push(tokens[i]);
}
const paragraphRaw = paragraphTextTokens.map((text) => text.raw).join("");
const paragraphText = paragraphTextTokens.map((text) => text.text).join("");
normalizedTokens.push({
type: "paragraph",
tokens: leadingCheckboxToken ? [leadingCheckboxToken, ...paragraphTextTokens] : paragraphTextTokens,
raw: paragraphRaw,
text: paragraphText
});
leadingCheckboxToken = null;
break;
}
case "checkbox":
break;
default: {
normalizedTokens.push(token);
}
}
}
} else {
normalizedTokens = tokens;
}
return normalizedTokens.map((token, index) => /* @__PURE__ */ jsxRuntime.jsx(MarkdownToken, { token, components }, index));
}
function assertTokens(_) {
}
function isBlockToken(token) {
return token.type === "paragraph" || token.type === "heading" || token.type === "blockquote" || token.type === "list_item";
}
function tokenize(markdown) {
return new marked.Lexer().lex(markdown);
}
function tokenizePartial(markdown) {
const preprocessedContent = trimPartialMarkdown(normalizeNewlines(markdown));
const tokens = tokenize(preprocessedContent);
try {
return completePartialTokens(tokens);
} catch {
return tokens;
}
}
function findPotentiallyPartialToken(tokens, parentToken) {
if (tokens.length === 0) {
return parentToken;
}
assertTokens(tokens);
const lastIndex = tokens.length - 1;
let lastToken = tokens[lastIndex];
if (lastToken.type === "space") {
const penultimateToken = tokens[lastIndex - 1];
if (!penultimateToken) {
return parentToken;
}
lastToken = penultimateToken;
}
if (lastToken.type === "list") {
const listToken = lastToken;
const lastListItem = listToken.items[listToken.items.length - 1];
if (!lastListItem) {
return parentToken;
}
const lastListItemTokens = lastListItem.tokens;
if (lastListItemTokens.some((token) => token.type === "space") && lastListItemTokens.length > 0) {
const lastListItemLastToken = lastListItemTokens[lastListItemTokens.length - 1];
if (lastListItemLastToken) {
if (lastListItemLastToken.type === "text") {
return lastListItemLastToken;
}
if (isBlockToken(lastListItemLastToken)) {
return findPotentiallyPartialToken(
lastListItemLastToken.tokens,
lastListItemLastToken
);
}
return void 0;
}
}
return findPotentiallyPartialToken(lastListItem.tokens, lastListItem);
}
if (lastToken.type === "table") {
const tableToken = lastToken;
const lastTableRow = tableToken.rows[tableToken.rows.length - 1];
if (!lastTableRow) {
return parentToken;
}
const firstEmptyTableCellIndex = lastTableRow.findIndex(
(cell) => cell.tokens.length === 0
);
const lastNonEmptyTableCell = firstEmptyTableCellIndex === -1 ? void 0 : firstEmptyTableCellIndex === 0 ? lastTableRow[firstEmptyTableCellIndex] : lastTableRow[firstEmptyTableCellIndex - 1];
if (!lastNonEmptyTableCell) {
return parentToken;
}
return findPotentiallyPartialToken(
lastNonEmptyTableCell.tokens,
lastNonEmptyTableCell
);
}
if (isBlockToken(lastToken)) {
return findPotentiallyPartialToken(lastToken.tokens, lastToken);
}
return parentToken;
}
function normalizeNewlines(string) {
return string.replace(NEWLINE_REGEX, "\n");
}
function trimPartialMarkdown(markdown) {
const lines = markdown.split("\n");
if (lines.length === 0) {
return markdown;
}
const [singleCharacterMatch] = lines[lines.length - 1].match(SINGLE_CHARACTER_REGEX) ?? [];
if (singleCharacterMatch) {
lines[lines.length - 1] = lines[lines.length - 1].slice(
0,
-singleCharacterMatch.length
);
return lines.join("\n");
}
const [bufferedCharactersMatch] = lines[lines.length - 1].match(BUFFERED_CHARACTERS_REGEX) ?? [];
if (bufferedCharactersMatch) {
lines[lines.length - 1] = lines[lines.length - 1].slice(
0,
-bufferedCharactersMatch.length
);
return lines.join("\n");
}
return markdown;
}
function completePartialInlineMarkdown(markdown, options = {}) {
const stack = [];
const allowLinksImages = options.allowLinksImages ?? true;
let completedMarkdown = markdown;
const partialEmojiMatch = completedMarkdown.match(PARTIAL_EMOJI_REGEX);
if (partialEmojiMatch) {
const partialEmoji = partialEmojiMatch[0];
completedMarkdown = completedMarkdown.slice(0, -partialEmoji.length);
if (partialEmoji.includes("\uFE0F") || partialEmoji.includes("\u20E3")) {
const codepoints = Array.from(completedMarkdown);
if (codepoints.length > 0) {
completedMarkdown = codepoints.slice(0, -1).join("");
}
}
}
for (let i = 0; i < completedMarkdown.length; i++) {
const character = completedMarkdown[i];
const isEscaped = i > 0 ? completedMarkdown[i - 1] === "\\" : false;
if (isEscaped) {
continue;
}
if (character === "`") {
const lastDelimiter = stack[stack.length - 1];
const isClosingPreviousDelimiter = lastDelimiter?.string === "`" && i > lastDelimiter.index;
if (isClosingPreviousDelimiter) {
stack.pop();
} else {
const characterAfterDelimiter = completedMarkdown[i + 1];
if (characterAfterDelimiter && !WHITESPACE_REGEX.test(characterAfterDelimiter)) {
stack.push({ string: "`", length: 1, index: i });
}
}
continue;
}
if (character === "*" || character === "_" || character === "~") {
const isInsideInlineCode = stack[stack.length - 1]?.string === "`";
let j = i;
while (j < completedMarkdown.length && completedMarkdown[j] === character) {
j++;
}
const consecutiveDelimiterCharacters = j - i;
if (isInsideInlineCode) {
i += consecutiveDelimiterCharacters - 1;
continue;
}
let remainingConsecutiveDelimiterCharacters = consecutiveDelimiterCharacters;
let consecutiveDelimiterCharacterIndex = 0;
while (remainingConsecutiveDelimiterCharacters > 0) {
const lastDelimiter = stack[stack.length - 1];
if (!lastDelimiter || lastDelimiter.string[0] !== character) {
break;
}
if (remainingConsecutiveDelimiterCharacters >= lastDelimiter.length) {
stack.pop();
remainingConsecutiveDelimiterCharacters -= lastDelimiter.length;
consecutiveDelimiterCharacterIndex += lastDelimiter.length;
continue;
}
break;
}
if (remainingConsecutiveDelimiterCharacters > 0) {
if (i + consecutiveDelimiterCharacters >= completedMarkdown.length) {
completedMarkdown = completedMarkdown.slice(
0,
completedMarkdown.length - remainingConsecutiveDelimiterCharacters
);
break;
}
const characterAfterDelimiters = completedMarkdown[i + consecutiveDelimiterCharacters];
if (characterAfterDelimiters && !WHITESPACE_REGEX.test(characterAfterDelimiters)) {
let delimiterStartIndex = i + consecutiveDelimiterCharacterIndex;
if (remainingConsecutiveDelimiterCharacters % 2 === 1) {
stack.push({
string: character,
length: 1,
index: delimiterStartIndex
});
delimiterStartIndex += 1;
remainingConsecutiveDelimiterCharacters -= 1;
}
while (remainingConsecutiveDelimiterCharacters >= 2) {
stack.push({
string: character + character,
length: 2,
index: delimiterStartIndex
});
delimiterStartIndex += 2;
remainingConsecutiveDelimiterCharacters -= 2;
}
}
}
i += consecutiveDelimiterCharacters - 1;
continue;
}
}
if (allowLinksImages) {
const partialLinkImageMatch = completedMarkdown.match(
PARTIAL_LINK_IMAGE_REGEX
);
if (partialLinkImageMatch) {
const linkImageStartIndex = partialLinkImageMatch.index;
const linkImageEndIndex = linkImageStartIndex + partialLinkImageMatch[0].length;
const isInsideInlineCode = stack.some(
(delimiter) => delimiter.string === "`" && delimiter.index < linkImageStartIndex
);
if (!isInsideInlineCode) {
const partialLinkImageContent = partialLinkImageMatch[0];
const {
text: partialLinkText,
url: partialLinkUrl,
image: isImage
} = partialLinkImageMatch.groups;
if (isImage) {
completedMarkdown = completedMarkdown.slice(
0,
-partialLinkImageContent.length
);
} else {
for (let i = stack.length - 1; i >= 0; i--) {
const delimiter = stack[i];
if (delimiter.index >= linkImageStartIndex && delimiter.index < linkImageEndIndex) {
stack.splice(i, 1);
}
}
const completedLinkText = partialLinkText ? partialLinkUrl ? (
// If there's a partial URL, the text is already completed.
partialLinkText
) : (
// Otherwise, we complete the text and its potential nested elements.
completePartialInlineMarkdown(partialLinkText, {
// Links/images cannot be nested.
allowLinksImages: false
})
) : "";
const completedLinkUrl = partialLinkUrl && !WHITESPACE_REGEX.test(partialLinkUrl) && core.isUrl(partialLinkUrl) ? (
// We only use the partial URL if it's valid.
partialLinkUrl
) : DEFAULT_PARTIAL_LINK_URL;
const completedLink = `[${completedLinkText}](${completedLinkUrl})`;
completedMarkdown = completedMarkdown.slice(
0,
-partialLinkImageContent.length
);
completedMarkdown += completedLink;
}
}
}
}
for (let i = stack.length - 1; i >= 0; i--) {
const delimiter = stack[i];
if (delimiter.index + delimiter.length >= completedMarkdown.length) {
completedMarkdown = completedMarkdown.slice(0, delimiter.index);
continue;
}
if (delimiter.string !== "`") {
completedMarkdown = completedMarkdown.trimEnd();
}
completedMarkdown += delimiter.string;
}
return completedMarkdown;
}
function completePartialTableMarkdown(markdown) {
const tableLines = markdown.split("\n");
if (tableLines.length === 0) {
return void 0;
}
const tableHeader = tableLines[0];
if (tableHeader === "|") {
return void 0;
}
const tableHeadings = tableHeader.split("|").map((cell) => cell.trim()).filter((cell) => cell !== "");
if (tableHeadings.length === 0) {
return void 0;
}
if (!tableHeader.endsWith("|")) {
const lastTableHeading = tableHeadings[tableHeadings.length - 1];
const completedLastTableHeading = completePartialInlineMarkdown(lastTableHeading);
tableHeadings[tableHeadings.length - 1] = completedLastTableHeading;
}
return `| ${tableHeadings.join(" | ")} |
| ${tableHeadings.map(() => "---").join(" | ")} |`;
}
function completePartialTokens(tokens) {
const potentiallyPartialToken = findPotentiallyPartialToken(tokens);
if (!potentiallyPartialToken) {
return tokens;
}
if (potentiallyPartialToken.type === "paragraph" || potentiallyPartialToken.type === "text") {
if (PARTIAL_TABLE_HEADER_REGEX.test(potentiallyPartialToken.raw)) {
const completedTableMarkdown = completePartialTableMarkdown(
potentiallyPartialToken.raw
);
if (completedTableMarkdown) {
const completedTable = tokenize(completedTableMarkdown)[0];
if (completedTable) {
const table = potentiallyPartialToken;
table.type = "table";
table.header = completedTable.header;
table.align = completedTable.align;
table.rows = completedTable.rows;
return tokens;
}
} else {
potentiallyPartialToken.text = "";
potentiallyPartialToken.tokens = [];
}
}
}
if (potentiallyPartialToken.type === "list_item") {
const listItem = potentiallyPartialToken;
const listItemTokens = listItem.tokens;
if (!listItem.task && listItemTokens.length === 1 && listItemTokens[0].type === "text") {
const listItemText = listItemTokens[0];
const checkboxMatch = listItemText.text.match(LIST_ITEM_CHECKBOX_REGEX);
if (checkboxMatch) {
listItem.task = true;
if (checkboxMatch[1] === "x") {
listItem.checked = true;
} else {
listItem.checked = false;
}
listItem.text = "";
listItem.tokens = [];
}
}
}
if (potentiallyPartialToken.text.length === 0) {
return tokens;
}
const completedMarkdown = completePartialInlineMarkdown(
potentiallyPartialToken.text
);
const completedMarkdownTokens = tokenize(completedMarkdown)[0]?.tokens ?? [];
potentiallyPartialToken.text = completedMarkdown;
potentiallyPartialToken.tokens = completedMarkdownTokens;
return tokens;
}
function parseHtmlEntities(input) {
const document = new DOMParser().parseFromString(
`<!doctype html><body>${input.replace(AMPERSAND_REGEX, "&").replace(LEFT_ANGLE_BRACKET_REGEX, "<").replace(RIGHT_ANGLE_BRACKET_REGEX, ">")}`,
"text/html"
);
return document.body.textContent;
}
function clampHeadingLevel(level) {
return Math.max(1, Math.min(6, level));
}
exports.Markdown = Markdown;
exports.MarkdownToken = MarkdownToken;
//# sourceMappingURL=Markdown.cjs.map