UNPKG

@bratislava/wysimark-editor

Version:

The customized fork of wysimark editor - A modern and clean rich text editor for React that supports 100% of CommonMark and GitHub Flavored Markdown.

1,684 lines (1,576 loc) 266 kB
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// react-shim.js var _react = require('react'); var _react2 = _interopRequireDefault(_react); // src/entry/index.tsx var _lodashthrottle = require('lodash.throttle'); var _lodashthrottle2 = _interopRequireDefault(_lodashthrottle); var _slate = require('slate'); var Slate = _interopRequireWildcard(_slate); var _slatereact = require('slate-react'); // src/entry/FullscreenWrap.tsx var _styled = require('@emotion/styled'); var _styled2 = _interopRequireDefault(_styled); var _clsx = require('clsx'); var _jsxruntime = require('react/jsx-runtime'); var $Container = _styled2.default.call(void 0, "div")` background: white; &.--fullscreen { position: fixed; top: 0; left: 0; right: 0; bottom: 0; z-index: 999; } `; var FullscreenWrap = ({ children, editor }) => { const [isFullscreen, setIsFullscreen] = _react.useState.call(void 0, false); _react.useEffect.call(void 0, () => { editor.fullscreen.toggleFullscreen = () => { setIsFullscreen((prev) => { editor.fullscreen.isFullscreen = !prev; return !prev; }); }; }, []); return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, $Container, { className: _clsx.clsx.call(void 0, { "--fullscreen": isFullscreen }), children }); }; // src/convert/parse/index.ts var _remarkgfm = require('remark-gfm'); var _remarkgfm2 = _interopRequireDefault(_remarkgfm); var _remarkparse = require('remark-parse'); var _remarkparse2 = _interopRequireDefault(_remarkparse); var _unified = require('unified'); // src/convert/utils.ts function assert(pass, message) { if (!pass) throw new Error(`${message}`); } function assertElementType(element, type) { if (element.type !== type) throw new Error( `Expected element to be of type ${JSON.stringify( element )} but is ${JSON.stringify(element, null, 2)}` ); } function assertUnreachable(x) { throw new Error( `Didn't expect to get here with value ${JSON.stringify(x, null, 2)}` ); } // src/convert/parse/parse-blockquote.ts function parseBlockquote(content) { return [{ type: "block-quote", children: parseContents(content.children) }]; } // src/convert/parse/parse-code-block.ts function parseCodeBlock(content) { const codeLines = content.value.split("\n"); return [ { type: "code-block", language: content.lang || "text", children: codeLines.map((codeLine) => ({ type: "code-block-line", children: [{ text: codeLine }] })) } ]; } // src/convert/parse/parse-footnote-definition.ts function parseFootnoteDefinition(footnote) { return [ { type: "block-quote", children: [ /** * Insert an initial paragraph with the footnote identifier in square * brackets. */ { type: "paragraph", children: [{ text: `[${footnote.identifier}]` }] }, /** * The rest of the children are parsed as is and supports the full range * of element types like headings, lists and nested block quotes. */ ...parseContents(footnote.children) ] } ]; } // src/convert/parse/parse-phrasing-content/normalize-segments.ts // src/convert/parse/parse-phrasing-content/normalize-segment.ts // src/convert/serialize/serialize-line/utils/is-utils.ts function isText(segment) { return Slate.Text.isText(segment); } function isElement(segment) { return Slate.Element.isElement(segment); } function isPlainSpace(segment) { return Slate.Text.isText(segment) && !!segment.text.match(/^\s+$/) && !segment.code; } // src/convert/serialize/serialize-line/utils/mark-utils/mark-convert-utils.ts var MARK_KEY_TO_TOKEN = { bold: "**", italic: "_", // ins: "++", strike: "~~", sup: "^", sub: "~", /** * IMPORTANT! * * We noop `code` here. * * We accept the `code` mark so as not to throw an error if it is found. We do * this because we handle `code` text specially because of the way it needs to * be escaped. * * This is handled in the `serializeLine` code. */ code: "" }; function convertMarkToSymbol(mark) { if (mark in MARK_KEY_TO_TOKEN) return MARK_KEY_TO_TOKEN[mark]; throw new Error( `Could not find mark ${JSON.stringify(mark)} in MARK_KEY_TO_TOKEN lookup` ); } function convertMarksToSymbolsExceptCode(marks) { return marks.map(convertMarkToSymbol).join(""); } // src/convert/serialize/serialize-line/utils/mark-utils/mark-get-utils.ts function getMarksFromText(text) { const { text: _, ...marks } = text; return Object.keys(marks); } function getMarksFromSegment(segment) { if (_slate.Text.isText(segment)) { if (isPlainSpace(segment)) { throw new Error( `You probably didn't mean to do this. We should only be getting marks from segments that are not plain space segments.` ); } return getMarksFromText(segment); } else if (segment.type === "anchor") { return getCommonAnchorMarks(segment.children); } else { throw new Error(`Unhandled type ${segment.type}`); } } function getCommonAnchorMarks(segments) { let commonMarks; for (const segment of segments) { if (!isText(segment)) { if (segment.type === "image-inline") continue; throw new Error( `Expected every segment in an anchor to be a Text segment` ); } if (isPlainSpace(segment)) continue; const currentMarks = getMarksFromText(segment); if (commonMarks === void 0) { commonMarks = currentMarks; continue; } commonMarks = commonMarks.filter( (commonMark) => currentMarks.includes(commonMark) ); } if (commonMarks === void 0) throw new Error( `No text segments were found as children in this anchor which should not be possible` ); return commonMarks; } // src/convert/serialize/serialize-line/utils/mark-utils/mark-order-utils.ts var ORDERED_MARK_KEYS = [ "bold", "italic", "underline", "strike", "sup", "sub", "code" ]; function sortMarks(marks) { return marks.slice().sort((a, b) => ORDERED_MARK_KEYS.indexOf(a) - ORDERED_MARK_KEYS.indexOf(b)); } // src/convert/serialize/serialize-line/utils/text-utils.ts var ESCAPES = [ "\\", // escape "`", // code "*", // bold/italic/hr "_", // bold/italic/hr "[", // link/list "]", // link/list "(", // link ")", // link "#", // headings "+", // list "-", // hr/list ".", // numbered list "!", // image "|", // table "^", // sup "~", // sub/strikethrough "<", // link/html ">", // link/html /** * Includes all the characters in the list of Backslash escapes in the example * for GitHub Flavored Markdown. * * https://github.github.com/gfm/#backslash-escapes */ "{", "}", "=", ":", ";", "$", "%", "&", "?", '"', "'", ",", "\\", "/", "@" ]; var ESCAPES_REGEXP = new RegExp( `(${ESCAPES.map((symbol) => `\\${symbol}`).join("|")})`, "g" ); function escapeText(s) { return s.replace(ESCAPES_REGEXP, (s2) => `\\${s2}`); } // src/convert/parse/parse-phrasing-content/normalize-segment.ts function areMarksEqual(a, b) { const marksA = getMarksFromText(a); const marksB = getMarksFromText(b); return marksA.length == marksB.length && marksA.every((v) => marksB.includes(v)); } function normalizeSegment(segment, mutablePrevSegment) { const segmentIsText = _slate.Text.isText(segment); const prevSegmentIsText = _slate.Text.isText(mutablePrevSegment); if (mutablePrevSegment && !prevSegmentIsText && !segmentIsText) { return [{ text: "" }, segment]; } if (!segmentIsText) return [segment]; if (mutablePrevSegment === void 0 || !prevSegmentIsText) return [segment]; const marksEqual = areMarksEqual(mutablePrevSegment, segment); if (marksEqual) { mutablePrevSegment.text = [mutablePrevSegment.text, segment.text].join(""); return []; } return [segment]; } // src/convert/parse/parse-phrasing-content/normalize-segments.ts function normalizeSegments(segments) { const nextSegments = []; for (let i = 0; i < segments.length; i++) { const mutablePrevSegment = nextSegments[nextSegments.length - 1]; nextSegments.push(...normalizeSegment(segments[i], mutablePrevSegment)); } if (nextSegments.length === 0) nextSegments.push({ text: "" }); if (!_slate.Text.isText(nextSegments[0])) nextSegments.unshift({ text: "" }); if (!_slate.Text.isText(nextSegments[nextSegments.length - 1])) nextSegments.push({ text: "" }); return nextSegments; } // src/convert/parse/parse-phrasing-content/parse-inline-image/parse-generic-image.ts function parseGenericImage(image) { return { url: image.url, title: image.title || void 0, alt: image.alt || void 0 }; } // src/convert/parseUrl.ts var URL_REGEX = /^(\/[^?#]*)(?:\?([^#]*))?(#.*)?$/; function parseUrl(url) { try { const urlData = new URL(url); return { origin: urlData.origin, hostname: urlData.hostname, pathname: urlData.pathname, searchParams: urlData.searchParams, hash: urlData.hash }; } catch (error) { const matchdata = url.match(URL_REGEX); if (matchdata === null) throw new Error(`Invalid format should not happen: ${url}`); const [_, pathname, searchParams, hash] = [...matchdata]; return { origin: "", hostname: "", pathname: pathname || "", searchParams: new URLSearchParams(searchParams), hash: hash || "" }; } } // src/convert/parse/parse-phrasing-content/parse-inline-image/parse-utils.ts function parseSize(s) { if (typeof s !== "string") return null; const sizeMatch = s.match(/^(\d+)x(\d+)$/); if (sizeMatch === null) return null; return { width: parseInt(sizeMatch[1]), height: parseInt(sizeMatch[2]) }; } // src/convert/parse/parse-phrasing-content/parse-inline-image/parse-portive-image.ts function parsePortiveImage(image) { const url = parseUrl(image.url); if (!url.hostname.match(/[.]portive[.]com$/i)) return; const sizeParam = url.searchParams.get("size"); if (sizeParam === null) return; const size = parseSize(sizeParam); if (size === null) return; const srcSizeMatch = url.pathname.match(/[-][-](\d+)x(\d+)[.][a-zA-Z]+$/); if (srcSizeMatch === null) return; return { url: `${url.origin}${url.pathname}`, title: image.title || void 0, alt: image.alt || void 0, width: size.width, height: size.height, srcWidth: parseInt(srcSizeMatch[1]), srcHeight: parseInt(srcSizeMatch[2]) }; } // src/convert/parse/parse-phrasing-content/parse-inline-image/parse-uncommon-mark-image.ts function parseUncommonMarkImage(image) { const url = parseUrl(image.url); if (url.hash.length === 0) return; const params = new URLSearchParams(url.hash.slice(1)); const size = parseSize(params.get("size")); const srcSize = parseSize(params.get("srcSize")); if (!size || !srcSize) return; return { url: `${url.origin}${url.pathname}`, title: image.title || void 0, alt: image.alt || void 0, width: size.width, height: size.height, srcWidth: srcSize.width, srcHeight: srcSize.height }; } // src/convert/parse/parse-phrasing-content/parse-inline-image/image-parsers.ts var imageParsers = [ parsePortiveImage, parseUncommonMarkImage, parseGenericImage ]; // src/convert/parse/parse-phrasing-content/parse-inline-image/index.ts function parseInlineImage(image) { for (const imageParser of imageParsers) { const imageData = imageParser(image); if (!imageData) continue; return [ { type: "image-inline", ...imageData, children: [{ text: "" }] } ]; } throw new Error(`Shouldn't get here because last parser always returns data`); } // src/convert/parse/parse-phrasing-content/parse-phrasing-content.ts function parsePhrasingContents(phrasingContents, marks = {}) { const segments = []; for (const phrasingContent of phrasingContents) { segments.push(...parsePhrasingContent(phrasingContent, marks)); } const nextInlines = normalizeSegments(segments); return nextInlines; } function parsePhrasingContent(phrasingContent, marks = {}) { switch (phrasingContent.type) { case "delete": return parsePhrasingContents(phrasingContent.children, { ...marks, strike: true }); case "emphasis": return parsePhrasingContents(phrasingContent.children, { ...marks, italic: true }); case "footnoteReference": return [{ text: `[${phrasingContent.identifier}]` }]; case "html": return [{ text: phrasingContent.value, code: true }]; case "image": return parseInlineImage(phrasingContent); case "inlineCode": { return [{ text: phrasingContent.value, ...marks, code: true }]; } case "link": return [ { type: "anchor", href: phrasingContent.url, title: ( /** * Ensure that `title` is undefined if it's null. */ phrasingContent.title == null ? void 0 : phrasingContent.title ), children: parsePhrasingContents(phrasingContent.children, marks) } ]; case "strong": return parsePhrasingContents(phrasingContent.children, { ...marks, bold: true }); case "text": return [{ text: phrasingContent.value, ...marks }]; case "linkReference": case "imageReference": throw new Error( `linkReference and imageReference should be converted to link and image through our transformInlineLinks function` ); case "break": return [{ text: "\n" }]; case "footnote": throw new Error("footnote is not supported yet"); } assertUnreachable(phrasingContent); } // src/convert/parse/parse-heading.ts function parseHeading(content) { return [ { type: "heading", level: content.depth, children: parsePhrasingContents(content.children) } ]; } // src/convert/parse/parse-html.ts function parseHTML(content) { return [ { type: "code-block", language: "html", children: content.value.split("\n").map((line) => ({ type: "code-block-line", children: [{ text: line }] })) } ]; } // src/convert/parse/parse-list/parse-list-item-child.ts function parseListItemChild(child, { depth, ordered, checked }) { switch (child.type) { case "paragraph": if (checked === true || checked === false) { return [ { type: "task-list-item", depth, checked, children: parsePhrasingContents(child.children) } ]; } else if (ordered) { return [ { type: "ordered-list-item", depth, children: parsePhrasingContents(child.children) } ]; } else { return [ { type: "unordered-list-item", depth, children: parsePhrasingContents(child.children) } ]; } case "list": return parseList(child, depth + 1); default: return parseContent(child); } } // src/convert/parse/parse-list/parse-list-item.ts function parseListItem(listItem, options) { const elements = []; for (const child of listItem.children) { elements.push( ...parseListItemChild(child, { ...options, checked: listItem.checked }) ); } return elements; } // src/convert/parse/parse-list/parse-list.ts function parseList(list, depth = 0) { const elements = []; for (const listItem of list.children) { elements.push( ...parseListItem(listItem, { depth, ordered: !!list.ordered }) ); } return elements; } // src/convert/parse/parse-paragraph.ts function isImageBlock(segments) { if (segments.length !== 3) return false; if (!("text" in segments[0]) || segments[0].text !== "") return false; if (!("text" in segments[2]) || segments[2].text !== "") return false; if (!("type" in segments[1]) || segments[1].type !== "image-inline") return false; return true; } var NBSP = "\xA0"; function isSingleNBSP(segments) { if (segments.length !== 1) return false; if (!("text" in segments[0]) || segments[0].text !== NBSP) return false; return true; } function parseParagraph(content) { const segments = parsePhrasingContents(content.children); if (isImageBlock(segments)) { const imageSegment = segments[1]; const imageBlockElement = { ...imageSegment, type: "image-block" }; return [imageBlockElement]; } if (isSingleNBSP(segments)) { return [ { type: "paragraph", children: [{ text: "" }] } ]; } return [ { type: "paragraph", children: segments } ]; } // src/convert/parse/parse-table.ts function parseTable(table) { if (table.align == null) throw new Error(`Expected an array of AlignType for table.align`); return [ { type: "table", columns: table.align.map((align) => ({ align: align || "left" })), children: table.children.map(parseTableRow) } ]; } function parseTableRow(row) { if (row.type !== "tableRow") throw new Error(`Expected a tableRow`); return { type: "table-row", children: row.children.map(parseTableCell) }; } function parseTableCell(cell) { if (cell.type !== "tableCell") throw new Error(`Expected a tableCell`); return { type: "table-cell", children: [ { type: "table-content", children: parsePhrasingContents(cell.children) } ] }; } // src/convert/parse/parse-thematic-break.ts function parseThematicBreak() { return [ { type: "horizontal-rule", children: [{ text: "" }] } ]; } // src/convert/parse/parse-content.ts function parseContents(contents) { const elements = []; for (const content of contents) { elements.push(...parseContent(content)); } return elements; } function parseContent(content) { switch (content.type) { case "blockquote": return parseBlockquote(content); case "code": return parseCodeBlock(content); case "definition": throw new Error(`The type "definition" should not exist. See comments`); case "footnoteDefinition": return parseFootnoteDefinition(content); case "heading": return parseHeading(content); case "html": return parseHTML(content); case "list": return parseList(content); case "paragraph": return parseParagraph(content); case "table": return parseTable(content); case "thematicBreak": return parseThematicBreak(); case "yaml": return []; } assertUnreachable(content); } // src/convert/parse/transform-inline-links.ts var _mdastutildefinitions = require('mdast-util-definitions'); var _unistutilvisit = require('unist-util-visit'); function transformInlineLinks(tree) { const definition = _mdastutildefinitions.definitions.call(void 0, tree); _unistutilvisit.visit.call(void 0, tree, (n, index, p) => { const node = n; const parent = p; if (node.type === "definition" && parent !== null && typeof index === "number") { parent.children.splice(index, 1); return [_unistutilvisit.SKIP, index]; } if (node.type === "imageReference" || node.type === "linkReference") { const identifier = "identifier" in node && typeof node.identifier === "string" ? node.identifier : ""; const def = definition(identifier); if (def && parent !== null && typeof index === "number") { const replacement = node.type === "imageReference" ? { type: "image", url: def.url, title: def.title, alt: node.alt } : { type: "link", url: def.url, title: def.title, children: node.children }; parent.children[index] = replacement; return [_unistutilvisit.SKIP, index]; } } }); } // src/convert/parse/index.ts var parser = _unified.unified.call(void 0, ).use(_remarkparse2.default).use(_remarkgfm2.default); function parseToAst(markdown) { const ast = parser.parse(markdown); transformInlineLinks(ast); return ast; } function parse(markdown) { const ast = parseToAst(markdown); if (ast.children.length === 0) { return [{ type: "paragraph", children: [{ text: "" }] }]; } return parseContents(ast.children); } // src/convert/serialize/normalize/normalizeElementListDepths.ts function isListItemElement(element) { return element.type === "ordered-list-item" || element.type === "unordered-list-item" || element.type === "task-list-item"; } function normalizeElementListDepths(elements) { const normalizedElements = []; let previousDepth = -1; for (const element of elements) { if (!isListItemElement(element)) { normalizedElements.push(element); previousDepth = -1; continue; } const nextDepth = element.depth > previousDepth + 1 ? previousDepth + 1 : element.depth; normalizedElements.push({ ...element, depth: nextDepth }); previousDepth = nextDepth; } return normalizedElements; } // src/convert/serialize/serialize-code-block/serialize-code-line.ts function serializeCodeLine(codeLine) { if (codeLine.type !== "code-block-line") throw new Error( `Expected all children of code-block to be a codeline but is ${JSON.stringify( codeLine, null, 2 )}` ); return codeLine.children.map((segment) => segment.text).join(""); } // src/convert/serialize/serialize-code-block/index.ts function serializeCodeBlock(codeBlock) { const lines = []; let backticks = 3; for (const codeLine of codeBlock.children) { const lineOfCode = serializeCodeLine(codeLine); const match = lineOfCode.match(/^([`]+)/); if (match) backticks = Math.max(backticks, match[1].length + 1); lines.push(lineOfCode); } lines.unshift(`${"`".repeat(backticks)}${codeBlock.language}`); lines.push(`${"`".repeat(backticks)}`); return `${lines.join("\n")} `; } // src/convert/serialize/serialize-image-shared/serialize-generic-image-url.ts function serializeGenericImageUrl(image) { return image.url; } // src/convert/serialize/serialize-image-shared/serialize-portive-image-url.ts function serializePortiveImageUrl(image) { if (image.url.startsWith("$")) return ""; const { hostname } = parseUrl(image.url); if (hostname.match(/[.]portive[.]com$/i) && image.width && image.height) return `${image.url}?size=${image.width}x${image.height}`; } // src/convert/serialize/serialize-image-shared/serialize-uncommonmark-image-url.ts function serializeUncommonmarkImageUrl(image) { if (image.width && image.height && image.srcWidth && image.srcHeight) return `${image.url}#srcSize=${image.srcWidth}x${image.srcHeight}&size=${image.width}x${image.height}`; } // src/convert/serialize/serialize-image-shared/index.ts var urlSerializers = [ serializePortiveImageUrl, serializeUncommonmarkImageUrl, serializeGenericImageUrl ]; function serializeImageShared(image) { for (const urlSerializer of urlSerializers) { const url = urlSerializer(image); if (typeof url === "string") { if (url === "") return ""; return `![${image.alt}](${url}${typeof image.title === "string" ? ` "${image.title}"` : ""})`; } } throw new Error(`Shouldn't get here`); } // src/convert/serialize/serialize-image-block/index.ts function serializeImageBlock(element) { const serializedImageShared = serializeImageShared(element); if (serializedImageShared === "") { return ""; } return `${serializedImageShared} `; } // src/convert/serialize/serialize-line/serialize-line.ts // src/convert/serialize/serialize-line/diff-marks/find-marks-to-add.ts function findMarksToAdd(orderedMarks, targetMarks) { const marksWeNeedToAdd = targetMarks.filter( (mark) => !orderedMarks.includes(mark) ); const orderedMarksToAdd = sortMarks(marksWeNeedToAdd); return { orderedMarksToAdd }; } // src/convert/serialize/serialize-line/diff-marks/find-marks-to-remove.ts function findMarksToRemove(orderedMarks, targetMarks) { const nextOrderedMarks = [...orderedMarks]; const marksWeNeedToRemove = orderedMarks.filter( (mark) => !targetMarks.includes(mark) ); const orderedMarksToRemove = []; for (let i = 0; i < orderedMarks.length; i++) { if (marksWeNeedToRemove.length === 0) break; const markToRemove = nextOrderedMarks.pop(); if (markToRemove === void 0) { throw new Error( `This shouldn't happen unless we made a mistake in the algorithm` ); } orderedMarksToRemove.push(markToRemove); const index = marksWeNeedToRemove.indexOf(markToRemove); if (index !== -1) { marksWeNeedToRemove.splice(index, 1); } } return { orderedMarksToRemove, nextOrderedMarks }; } // src/convert/serialize/serialize-line/diff-marks/index.ts function diffMarks(orderedMarks, targetMarks) { const { orderedMarksToRemove, nextOrderedMarks } = findMarksToRemove( orderedMarks, targetMarks ); const { orderedMarksToAdd } = findMarksToAdd(nextOrderedMarks, targetMarks); return { remove: orderedMarksToRemove, add: orderedMarksToAdd, nextOrderedMarks: [...nextOrderedMarks, ...orderedMarksToAdd] }; } // src/convert/serialize/serialize-line/normalize-line/index.ts // src/convert/serialize/serialize-line/normalize-line/normalizers/merge-adjacent-spaces.ts function mergeAdjacentSpaces({ node, nextNode, nodes, index }) { if (!isText(node) || !isPlainSpace(node) || node.code) return false; if (!isText(nextNode) || !isPlainSpace(nextNode) || node.code) return false; nodes.splice(index, 2, { text: `${node.text}${nextNode.text}` }); return true; } // src/convert/serialize/serialize-line/normalize-line/normalizers/move-spaces-out-of-anchors.ts function moveSpacesAtStartOfAnchor({ node, nodes, prevNode, index }) { if (!isElement(node)) return false; if (node.type !== "anchor") return false; node; const firstChild = node.children[0]; if (isText(firstChild) && isPlainSpace(firstChild)) { node.children.splice(0, 1); if (isText(prevNode) && isPlainSpace(prevNode)) { prevNode.text = `${prevNode.text}${firstChild.text}`; } else { nodes.splice(index, 0, { text: firstChild.text }); } return true; } return false; } function moveSpacesAtEndOfAnchor({ node, nodes, nextNode, index }) { if (!isElement(node)) return false; if (node.type !== "anchor") return false; node; const lastChild = node.children[node.children.length - 1]; if (isText(lastChild) && isPlainSpace(lastChild)) { node.children.splice(node.children.length - 1, 1); if (isText(nextNode) && isPlainSpace(nextNode)) { nextNode.text = `${lastChild.text}${nextNode.text}`; } else { nodes.splice(index + 1, 0, { text: lastChild.text }); } return true; } return false; } // src/convert/serialize/serialize-line/normalize-line/normalizers/must-have-one-text-child.ts function mustHaveOneTextChild({ node }) { if (!isElement(node)) return false; if (node.type !== "line") return false; if (node.children.length > 0) return false; node.children.push({ text: "" }); return true; } // src/convert/serialize/serialize-line/normalize-line/normalizers/slice-spaces-at-node-boundaries.ts function sliceSpacesAtNodeBoundaries({ node, nodes, index }) { if (!isText(node)) return false; if (isPlainSpace(node)) return false; if (node.code) return false; const match = node.text.match(/^(\s*)(.*?)(\s*)$/); if (!match) return false; if (match[1].length === 0 && match[3].length === 0) return false; const nextSegments = [ { text: match[1] }, { ...node, text: match[2] }, { text: match[3] } ].filter((text) => text.text !== ""); nodes.splice(index, 1, ...nextSegments); return true; } // src/convert/serialize/serialize-line/normalize-line/normalizers/trim-spaces-at-end-of-line.ts function trimSpaceAtEndOfLine({ index, nodes, node, parent }) { if (index !== nodes.length - 1) return false; if (nodes.length <= 1) return false; if (!isText(node)) return false; if (!isPlainSpace(node)) return false; if (parent && isElement(parent) && parent.type === "line") { nodes.splice(nodes.length - 1, 1); return true; } return false; } // src/convert/serialize/serialize-line/normalize-line/normalizers/trim-spaces-at-start-of-line.ts function trimSpaceAtStartOfLine({ index, nodes, node, parent }) { if (index !== 0) return false; if (nodes.length === 0) return false; if (!isText(node)) return false; if (!isPlainSpace(node)) return false; if (parent && isElement(parent) && parent.type === "line") { nodes.splice(0, 1); return true; } return false; } // src/convert/serialize/serialize-line/normalize-line/normalizers/index.ts var normalizers = [ sliceSpacesAtNodeBoundaries, moveSpacesAtStartOfAnchor, moveSpacesAtEndOfAnchor, mergeAdjacentSpaces, trimSpaceAtStartOfLine, trimSpaceAtEndOfLine, mustHaveOneTextChild ]; // src/convert/serialize/serialize-line/normalize-line/run-normalizers-on-node.ts function runNormalizersOnNode(normalizeOptions) { for (const normalizer of normalizers) { const isHandled = normalizer(normalizeOptions); if (isHandled) { return true; } } return false; } // src/convert/serialize/serialize-line/normalize-line/normalize-nodes.ts var MAX_RERUNS = 72; function normalizeNodes(nodes, parent) { let isAnyUpdated = false; let isUpdated; let runs = 0; const maxReruns = (nodes.length + 1) * MAX_RERUNS; do { isUpdated = false; runs = runs + 1; if (runs > maxReruns) throw new Error( `There have been ${runs} normalization passes (72x the number of nodes at this level). This likely indicates a bug in the code.` ); segmentLoop: for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; if (isElement(node)) { const isChildrenUpdated = normalizeNodes( node.children, node ); if (isChildrenUpdated) { isUpdated = true; isAnyUpdated = true; break segmentLoop; } } const prevNode = nodes[i - 1]; const nextNode = nodes[i + 1]; const options = { parent, node, prevNode, nextNode, index: i, nodes }; if (runNormalizersOnNode(options)) { isUpdated = true; isAnyUpdated = true; break segmentLoop; } } } while (isUpdated); return isAnyUpdated; } // src/convert/serialize/serialize-line/normalize-line/index.ts var duplicateSegments = (segments) => { return segments.map((segment) => { if (_slate.Element.isElement(segment) && segment.type === "anchor") { return { ...segment, children: duplicateSegments(segment.children) }; } else { return segment; } }); }; function normalizeLine(segments) { const line = { type: "line", children: duplicateSegments(segments) }; normalizeNodes([line], void 0); return line.children; } // src/convert/serialize/serialize-line/segment/serialize-segment.ts // src/convert/serialize/serialize-line/segment/serialize-code-text.ts function serializeCodeText(text) { let max = 0; for (const match of text.text.matchAll(/[`]+/g)) { max = Math.max(max, match[0].length); } if (max === 0) return `\`${text.text.replace(/[`]/g, "\\`")}\``; return `${"`".repeat(max + 1)} ${text.text} ${"`".repeat(max + 1)}`; } // src/convert/serialize/serialize-line/segment/serialize-anchor.ts function escapeTitle(title) { return title.replace(/"/g, '\\"'); } function serializeAnchor(anchor) { const commonAnchorMarks = getCommonAnchorMarks(anchor.children); if (anchor.href.startsWith("$")) return serializeLine( anchor.children, commonAnchorMarks, commonAnchorMarks ); if (typeof anchor.title === "string" && anchor.title.length > 0) { return ( /** * TODO: Handle anchor children more elegantly in serializeAnchor. * * We type cast `children` as `Segment` here because the children of an * `anchor` is limited to be Inline types. There are two things to do * related to this though: * * - [ ] consider fixing the `anchor` type to actually limit the * children as expected. * - [ ] consider expanding the definition of `Segment` to include * inline images as that is an acceptable inline value which is * currently not defined as part of Segment. */ `[${serializeLine( anchor.children, commonAnchorMarks, commonAnchorMarks )}](${anchor.href} "${escapeTitle(anchor.title)}")` ); } else { return ( /** * TODO: Handle anchor children more elegantly in serializeAnchor. * * We type cast `children` as `Segment` here because the children of an * `anchor` is limited to be Inline types. There are two things to do * related to this though: * * - [ ] consider fixing the `anchor` type to actually limit the * children as expected. * - [ ] consider expanding the definition of `Segment` to include * inline images as that is an acceptable inline value which is * currently not defined as part of Segment. */ `[${serializeLine( anchor.children, commonAnchorMarks, commonAnchorMarks )}](${anchor.href})` ); } } // src/convert/serialize/serialize-line/segment/serialize-non-code-text.ts function serializeNonCodeText(text) { return escapeText(text.text); } // src/convert/serialize/serialize-line/segment/serialize-segment.ts function serializeSegment(segment) { if (_slate.Text.isText(segment)) { if (segment.code) return serializeCodeText(segment); return serializeNonCodeText(segment); } switch (segment.type) { case "anchor": { return serializeAnchor(segment); } case "image-inline": return serializeImageShared(segment); default: assertUnreachable(segment); } } // src/convert/serialize/serialize-line/serialize-line.ts function serializeLine(inputSegments, leadingMarks = [], trailingMarks = []) { const segments = normalizeLine(inputSegments); const substrings = []; let leadingDiff = diffMarks(leadingMarks, getMarksFromSegment(segments[0])); for (let i = 0; i < segments.length; i++) { const segment = segments[i]; if (_slate.Text.isText(segment) && isPlainSpace(segment)) { substrings.push(segment.text); continue; } substrings.push(convertMarksToSymbolsExceptCode(leadingDiff.add)); substrings.push(serializeSegment(segment)); const nextMarks = getNextMarks(segments, i, trailingMarks); const trailingDiff = diffMarks(leadingDiff.nextOrderedMarks, nextMarks); substrings.push(convertMarksToSymbolsExceptCode(trailingDiff.remove)); leadingDiff = trailingDiff; } return substrings.join(""); } function getNextMarks(segments, index, trailingMarks) { for (let i = index + 1; i < segments.length; i++) { const segment = segments[i]; if (isPlainSpace(segment)) continue; if (_slate.Element.isElement(segment) && segment.type === "image-inline") continue; return getMarksFromSegment(segment); } return trailingMarks; } // src/convert/serialize/serialize-table/index.ts function serializeTable(element) { const lines = []; lines.push(serializeTableRow(element.children[0])); lines.push(serializeColumns(element.columns)); element.children.slice(1).forEach((row) => { lines.push(serializeTableRow(row)); }); return `${lines.join("\n")} `; } function serializeColumns(columns) { const isAllLeft = columns.every((column) => column.align === "left"); if (isAllLeft) { return `|${columns.map(() => "---").join("|")}|`; } return `|${columns.map((column) => serializeAlign(column.align)).join("|")}|`; } function serializeAlign(align) { switch (align) { case "left": return ":---"; case "center": return ":---:"; case "right": return "---:"; } } function serializeTableRow(element) { assertElementType(element, "table-row"); return `|${element.children.map(serializeTableCell).join("|")}|`; } function serializeTableCell(element) { assertElementType(element, "table-cell"); assert( element.children.length === 1, `Expected table-cell to have one child but is ${JSON.stringify( element.children )}` ); return element.children.map(serializeTableContent).join(); } function serializeTableContent(element) { assertElementType(element, "table-content"); return serializeLine(element.children); } // src/convert/serialize/serialize-element.ts var LIST_INDENT_SIZE = 4; function serializeElement(element, orders) { switch (element.type) { case "anchor": return `[${serializeLine(element.children)}](${element.href})`; case "block-quote": { const lines = serializeElements(element.children); return `${lines.split("\n").map((line) => `> ${line}`.trim()).join("\n")} `; } case "code-block": return serializeCodeBlock(element); case "code-block-line": throw new Error( `code-block-line should only be present as child of code-block` ); case "heading": return `${"#".repeat(element.level)} ${serializeLine( element.children )} `; case "horizontal-rule": return "---\n\n"; case "paragraph": return `${serializeLine(element.children)} `; case "table": return serializeTable(element); case "table-row": case "table-cell": case "table-content": throw new Error( `Table elements should only be present as children of table which should be handled by serializeTable. Got ${element.type} may indicate an error in normalization.` ); case "unordered-list-item": { const indent2 = " ".repeat(element.depth * LIST_INDENT_SIZE); return `${indent2}- ${serializeLine(element.children)} `; } case "ordered-list-item": { const indent2 = " ".repeat(element.depth * LIST_INDENT_SIZE); return `${indent2}${orders[element.depth]}. ${serializeLine( element.children )} `; } case "task-list-item": { const indent2 = " ".repeat(element.depth * LIST_INDENT_SIZE); let line = serializeLine(element.children); if (line.trim() === "") { line = "&#32;"; } return `${indent2}- [${element.checked ? "x" : " "}] ${line} `; } case "image-block": return serializeImageBlock(element); case "image-inline": throw new Error( `This shouldn't happen because inlines are handled in serializeSegment` ); } assertUnreachable(element); } // src/convert/serialize/serialize-elements.ts function serializeElements(elements) { const segments = []; let orders = []; for (const element of elements) { if (element.type === "ordered-list-item") { orders[element.depth] = (orders[element.depth] || 0) + 1; orders = orders.slice(0, element.depth + 1); } else if (element.type === "unordered-list-item" || element.type === "task-list-item") { orders = orders.slice(0, element.depth); } else { orders = []; } segments.push(serializeElement(element, orders)); } const joined = segments.join(""); if (joined.trim() === "") return ""; return replaceConsecutiveNewlines(replaceLeadingNewlines(joined)).trim(); } function replaceLeadingNewlines(input) { return input.replace(/^\n\n/g, "&nbsp;\n\n"); } function replaceConsecutiveNewlines(input) { return input.replace(/(\n{4,})/g, (match) => { const newlineCount = match.length; const count = Math.floor((newlineCount - 2) / 2); return "\n\n" + Array(count).fill("&nbsp;").join("\n\n") + "\n\n"; }); } // src/convert/serialize/index.ts function serialize(elements) { const normalizedElements = normalizeElementListDepths(elements); return serializeElements(normalizedElements); } // src/sink/create-plugin/index.ts var createPlugin = (fn) => { return { fn }; }; // src/sink/editable/index.tsx // src/sink/editable/utils.ts function defined(value) { return !!value; } // src/sink/editable/create-decorate.ts function createDecorate(originalFn, plugins2) { const fns = plugins2.map((plugin) => _optionalChain([plugin, 'access', _2 => _2.editableProps, 'optionalAccess', _3 => _3.decorate])).filter(defined); return function(entry) { const ranges = []; for (const fn of fns) { const resultRanges = fn(entry); ranges.push(...resultRanges); } if (originalFn) ranges.push(...originalFn(entry)); return ranges; }; } // src/sink/editable/create-editable.tsx function createEditable(plugins2) { const fns = plugins2.map((plugin) => plugin.renderEditable).filter(defined); let CurrentRenderEditable = (props) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _slatereact.Editable, { ...props }); for (const fn of fns) { const PrevRenderEditable = CurrentRenderEditable; CurrentRenderEditable = (props) => { return fn({ attributes: props, Editable: PrevRenderEditable }); }; } return CurrentRenderEditable; } // src/sink/editable/create-handler.ts function extractEditableFns(plugins2, key2) { const fns = []; for (const plugin of plugins2) { const maybeFn = _optionalChain([plugin, 'access', _4 => _4.editableProps, 'optionalAccess', _5 => _5[key2]]); if (maybeFn) fns.push(maybeFn); } return fns; } function createHandlerFn(fns, originalFn) { return function(event) { for (const fn of fns) { if (fn(event)) return; } _optionalChain([originalFn, 'optionalCall', _6 => _6(event)]); }; } var createOnKeyDown = (originalFn, plugins2) => { const fns = extractEditableFns(plugins2, "onKeyDown"); return createHandlerFn(fns, originalFn); }; var createOnKeyUp = (originalFn, plugins2) => { const fns = extractEditableFns(plugins2, "onKeyUp"); return createHandlerFn(fns, originalFn); }; var createOnPaste = (originalFn, plugins2) => { const fns = extractEditableFns(plugins2, "onPaste"); return createHandlerFn(fns, originalFn); }; var createOnDrop = (originalFn, plugins2) => { const fns = extractEditableFns(plugins2, "onDrop"); return createHandlerFn(fns, originalFn); }; // src/sink/editable/create-render-element.ts function createRenderElement(originalFn, plugins2) { const fns = plugins2.map((plugin) => _optionalChain([plugin, 'access', _7 => _7.editableProps, 'optionalAccess', _8 => _8.renderElement])).filter(defined); return function renderElement5(renderElementProps) { for (const fn of fns) { const result = fn(renderElementProps); if (result) return result; } if (originalFn === void 0) { throw new Error( `Element with type ${renderElementProps.element.type} not handled. Note that renderElement is not defined on SinkEditable so this is only the result of checking the Sink Plugins.` ); } return originalFn(renderElementProps); }; } // src/sink/editable/create-render-leaf.ts function createRenderLeaf(originalFn, plugins2) { if (originalFn === void 0) { throw new Error(`renderLeaf was not defined on SinkEditable`); } const fns = plugins2.map((plugin) => _optionalChain([plugin, 'access', _9 => _9.editableProps, 'optionalAccess', _10 => _10.renderLeaf])).filter(defined).reverse(); return function(renderLeafProps) { let value = originalFn({ ...renderLeafProps, /** * We override this because `attributes` should only appear on the * uppermost leaf element if there are several nested ones and it's * possible that this won't be the uppermost leaf. * * We add attributes back on at the very end so no need to worry if * we omit it here. */ attributes: {} }); for (const fn of fns) { const possibleValue = fn({ ...renderLeafProps, children: value }); if (possibleValue) { value = possibleValue; } } value = _react.cloneElement.call(void 0, value, renderLeafProps.attributes); return value; }; } // src/sink/editable/create-render-placeholder.tsx function createRenderPlaceholder(originalFn, plugins2) { if (originalFn) return originalFn; const fns = plugins2.map((plugin) => _optionalChain([plugin, 'access', _11 => _11.editableProps, 'optionalAccess', _12 => _12.renderPlaceholder])).filter(defined); if (fns.length === 0) return void 0; return function(renderPlaceholderProps) { if (fns.length > 1) { throw new Error( `Only one plugin can define renderPlaceholder but there are ${fns.length}` ); } const fn = fns[0]; if (fn == null) throw new Error(`Expected fn to be defined`); return fn(renderPlaceholderProps); }; } // src/sink/editable/styles.tsx var SinkReset = _styled2.default.call(void 0, "div")` -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; font-size: 16px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; box-sizing: border-box; `; // src/sink/editable/index.tsx function SinkEditable(originalProps) { const editor = _slatereact.useSlateStatic.call(void 0, ); _react.useEffect.call(void 0, () => { _slate.Editor.normalize(editor, { force: true }); }, []); const { plugins: plugins2 } = editor.sink; const nextProps = _react.useMemo.call(void 0, () => ({ ...originalProps, decorate: createDecorate(originalProps.decorate, plugins2), renderElement: createRenderElement(originalProps.renderElement, plugins2), renderLeaf: createRenderLeaf(originalProps.renderLeaf, plugins2), renderPlaceholder: createRenderPlaceholder( originalProps.renderPlaceholder, plugins2 ), /** * NOTE: We skip `onKeyUp` as it is deprecated. If somebody needs it in new * code, we can add it back in. * * https://developer.mozilla.org/en-US/docs/Web/API/Element/keypress_event */ onKeyDown: createOnKeyDown(originalProps.onKeyDown, plugins2), onKeyUp: createOnKeyUp(originalProps.onKeyUp, plugins2), onPaste: createOnPaste(originalProps.onPaste, plugins2), onDrop: createOnDrop(originalProps.onDrop, plugins2) }), Object.values(originalProps) ); const NextEditable = _react.useMemo.call(void 0, () => createEditable(plugins2), [plugins2]); return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, NextEditable, { ...nextProps }); } // src/sink/editor/create-boolean-action.ts function createBooleanAction(editor, actionKey, plugins2) { const originalAction = editor[actionKey]; const actionPlugins = plugins2.filter((plugin) => _optionalChain([plugin, 'access', _13 => _13.editor, 'optionalAccess', _14 => _14[actionKey]])); return function nextBooleanAction(node) { for (const plugin of actionPlugins) { const result = _optionalChain([plugin, 'access', _15 => _15.editor, 'optionalAccess', _16 => _16[actionKey], 'optionalCall', _17 => _17(node)]); if (typeof result === "boolean") return result; } return originalAction(node); }; } // src/sink/editor/create-void-action.ts function createVoidAction(editor, actionKey, plugins2) { const originalAction = editor[actionKey]; const actionPlugins = plugins2.filter((plugin) => _optionalChain([plugin, 'access', _18 => _18.editor, 'optionalAccess', _19 => _19[actionKey]])); return function nextVoidAction(...args) { let isHandled = false; const afterHandledCallbacks = []; for (const plugin of actionPlugins) { const response = _optionalChain([plugin, 'access', _20 => _20.editor, 'optionalAccess', _21 => _21[actionKey], 'optionalCall', _22 => _22(...args)]); if (typeof response === "function") { afterHandledCallbacks.push(response); } else if (response === true) { isHandled = true; break; } } if (!isHandled) { originalAction(...args); } afterHandledCallbacks.forEach((callback) => callback()); }; } // src/sink/editor/index.ts function createWithSink(pluginFns) { return (originalEditor, options) => { const editor = originalEditor; const plugins2 = pluginFns.map( (plugin) => plugin(editor, options, { createPolicy: (x) => x }) ); editor.sink = { plug