@localix-ai/flowdown 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/dist/core/blocks.js +3 -2
- package/dist/core/blocks.js.map +1 -1
- package/dist/core/continues.js +15 -0
- package/dist/core/continues.js.map +1 -0
- package/dist/core/fence-scan.js +10 -1
- package/dist/core/fence-scan.js.map +1 -1
- package/dist/core/file-tree-parse.js +1 -1
- package/dist/core/file-tree-parse.js.map +1 -1
- package/dist/core/heal.js +1 -1
- package/dist/core/heal.js.map +1 -1
- package/dist/document/fence.js +3 -2
- package/dist/document/fence.js.map +1 -1
- package/dist/dom/index.js +25 -7
- package/dist/dom/index.js.map +1 -1
- package/dist/element/index.d.ts +6 -0
- package/dist/element/index.js.map +1 -1
- package/dist/engine/blocks.js +11 -0
- package/dist/engine/blocks.js.map +1 -1
- package/dist/engine/inlines.js +40 -5
- package/dist/engine/inlines.js.map +1 -1
- package/dist/react/code-block.js +15 -2
- package/dist/react/code-block.js.map +1 -1
- package/dist/react/draw.js +1 -0
- package/dist/react/draw.js.map +1 -1
- package/dist/react/markdown.js +4 -2
- package/dist/react/markdown.js.map +1 -1
- package/dist/react/scrolls.js +62 -0
- package/dist/react/scrolls.js.map +1 -0
- package/dist/react/table.js +6 -20
- package/dist/react/table.js.map +1 -1
- package/dist/react/use-smooth-text.js +2 -1
- package/dist/react/use-smooth-text.js.map +1 -1
- package/dist/render/labels.d.ts +5 -0
- package/dist/render/labels.js +3 -0
- package/dist/render/labels.js.map +1 -1
- package/dist/render/tree.js +2 -0
- package/dist/render/tree.js.map +1 -1
- package/dist/render/view.d.ts +4 -1
- package/dist/render/view.js +6 -2
- package/dist/render/view.js.map +1 -1
- package/dist/render/views.js +2 -2
- package/dist/render/views.js.map +1 -1
- package/dist/styles.css +12 -1
- package/dist/tui/wrap.js +5 -4
- package/dist/tui/wrap.js.map +1 -1
- package/package.json +3 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inlines.js","names":[],"sources":["../../src/engine/inlines.ts"],"sourcesContent":["/**\n * The inline pass: the text of one block read into emphasis, links, code spans and the rest, by\n * the algorithm in the CommonMark spec (a delimiter stack for emphasis, a bracket stack for\n * links). One pass over the text; the parser's state lives in the module, since a block's text\n * is read from start to end before the next one begins.\n */\nimport { decodeEntity, ENTITY, isEscapable, normalizeUri, unescapeString } from './entities'\nimport type { InlineRule, InlineState, Node, Reference } from './types'\n\nexport interface InlineContext {\n refs: Map<string, Reference>\n /** The footnotes the document defines: normalized label to the label as written. */\n footnotes: Map<string, string>\n /** How many times each footnote has been referred to so far, in the order first referred to. */\n footnoteUses: Map<string, number>\n gfm: boolean\n /** Whether `$$` alone on a line fences a display formula. */\n math: boolean\n /** Extension rules by the ASCII code of the character that starts them. */\n rules: (InlineRule['parse'][] | undefined)[]\n /** 1 for every ASCII character that can start something other than plain text. */\n special: Uint8Array\n}\n\n/** A node while its block is read: linked to its siblings, so emphasis can wrap a run of them. */\ninterface Inl {\n type: string\n value: string\n parent: Inl | null\n prev: Inl | null\n next: Inl | null\n first: Inl | null\n last: Inl | null\n url: string\n title: string\n /** The finished node of an extension's construct. */\n node: Node | null\n}\n\ninterface Delimiter {\n cc: number\n num: number\n orig: number\n node: Inl\n prev: Delimiter | null\n next: Delimiter | null\n canOpen: boolean\n canClose: boolean\n}\n\ninterface Bracket {\n node: Inl\n prev: Bracket | null\n prevDelimiter: Delimiter | null\n index: number\n image: boolean\n active: boolean\n bracketAfter: boolean\n}\n\n/** A bare address found in the text: where it is and where it leads. */\ninterface Span {\n start: number\n end: number\n url: string\n}\n\nconst C_NEWLINE = 10\nconst C_SPACE = 32\nconst C_BANG = 33\nconst C_AMPERSAND = 38\nconst C_OPEN_PAREN = 40\nconst C_CLOSE_PAREN = 41\nconst C_ASTERISK = 42\nconst C_COLON = 58\nconst C_LESSTHAN = 60\nconst C_OPEN_BRACKET = 91\nconst C_BACKSLASH = 92\nconst C_CLOSE_BRACKET = 93\nconst C_CARET = 94\nconst C_UNDERSCORE = 95\nconst C_BACKTICK = 96\nconst C_TILDE = 126\n\nconst TAGNAME = '[A-Za-z][A-Za-z0-9-]*'\nconst ATTRIBUTENAME = '[a-zA-Z_:][a-zA-Z0-9:._-]*'\nconst ATTRIBUTEVALUE = '(?:[^\"\\'=<>`\\\\x00-\\\\x20]+|\\'[^\\']*\\'|\"[^\"]*\")'\nconst ATTRIBUTE = `(?:\\\\s+${ATTRIBUTENAME}(?:\\\\s*=\\\\s*${ATTRIBUTEVALUE})?)`\nexport const OPENTAG = `<${TAGNAME}${ATTRIBUTE}*\\\\s*/?>`\nexport const CLOSETAG = `</${TAGNAME}\\\\s*[>]`\nconst HTMLTAG = `(?:${OPENTAG}|${CLOSETAG}|<!-->|<!--->|<!--[\\\\s\\\\S]*?-->|[<][?][\\\\s\\\\S]*?[?][>]|<![A-Za-z]+[^>]*>|<!\\\\[CDATA\\\\[[\\\\s\\\\S]*?\\\\]\\\\]>)`\n\nconst reHtmlTag = new RegExp(HTMLTAG, 'y')\nconst reEntity = new RegExp(ENTITY, 'iy')\n// The specification's own character classes name control characters.\n// oxlint-disable-next-line no-control-regex\nconst reAutolink = /<([A-Za-z][A-Za-z0-9.+-]{1,31}:[^<>\\x00-\\x20]*)>/y\nconst reEmailAutolink =\n /<([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/y\nconst reLinkLabel = /\\[(?:[^\\\\[\\]]|\\\\[\\s\\S]){0,1000}\\]/y\n// oxlint-disable-next-line no-control-regex\nconst reLinkDestinationBraces = /<(?:[^<>\\n\\\\\\x00]|\\\\.)*>/y\nconst reLinkTitle = /\"(?:\\\\[\\s\\S]|[^\"\\\\])*\"|'(?:\\\\[\\s\\S]|[^'\\\\])*'|\\((?:\\\\[\\s\\S]|[^()\\\\])*\\)/y\nconst reSpaceAtEndOfLine = /[ \\t]*(?:\\n|$)/y\nconst reFootnoteReference = /\\[\\^([^\\]\\s]+)\\]/y\nconst reUnicodeSpace = /\\p{Zs}/u\nconst reUnicodePunctuation = /[\\p{P}\\p{S}]/u\n/** Bare addresses: a URL with a scheme, one that starts `www.`, or an email address. */\nconst reBareLink =\n /(?:(?:https?|ftp):\\/\\/|www\\.)[^\\s<]*|(mailto:|xmpp:)?[A-Za-z0-9._+-]+@[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)+(\\/[A-Za-z0-9@.]+)?/g\n\n/** Whether the text may hold a bare address: three searches for a few characters, no regex. */\nconst mayHoldBareLink = (text: string): boolean =>\n text.indexOf('@') !== -1 || text.indexOf('://') !== -1 || text.indexOf('www.') !== -1\n\nlet subject = ''\nlet pos = 0\nlet end = 0\nlet ctx: InlineContext\nlet root: Inl\nlet delimiters: Delimiter | null = null\nlet brackets: Bracket | null = null\n/** Whether the text may hold a bare address (`www.`, `://`, `@`) to turn into a link. */\nlet bareLinks = false\n/** Lengths of backtick runs known to have no closing run in the rest of the text. */\nlet noBacktickCloser: Set<number> | null = null\n\nconst inl = (type: string, value = ''): Inl => ({\n type,\n value,\n parent: null,\n prev: null,\n next: null,\n first: null,\n last: null,\n url: '',\n title: '',\n node: null,\n})\n\nconst append = (parent: Inl, child: Inl): void => {\n child.parent = parent\n child.prev = parent.last\n child.next = null\n if (parent.last) parent.last.next = child\n else parent.first = child\n parent.last = child\n}\n\nconst unlink = (node: Inl): void => {\n if (node.prev) node.prev.next = node.next\n else if (node.parent) node.parent.first = node.next\n if (node.next) node.next.prev = node.prev\n else if (node.parent) node.parent.last = node.prev\n node.parent = node.prev = node.next = null\n}\n\nconst insertAfter = (node: Inl, sibling: Inl): void => {\n sibling.parent = node.parent\n sibling.prev = node\n sibling.next = node.next\n if (node.next) node.next.prev = sibling\n else if (node.parent) node.parent.last = sibling\n node.next = sibling\n}\n\nconst text = (value: string): Inl => {\n const node = inl('text', value)\n append(root, node)\n return node\n}\n\n/** Runs a sticky expression at the current place; the match, with the place moved past it. */\nconst match = (re: RegExp): string | null => {\n re.lastIndex = pos\n const found = re.exec(subject)\n if (found === null) return null\n pos = re.lastIndex\n return found[0]\n}\n\n/** Skips spaces, tabs and at most one line ending. */\nconst spnl = (): void => {\n let code = subject.charCodeAt(pos)\n while (code === C_SPACE || code === 9) code = subject.charCodeAt(++pos)\n if (code === C_NEWLINE) {\n code = subject.charCodeAt(++pos)\n while (code === C_SPACE || code === 9) code = subject.charCodeAt(++pos)\n }\n}\n\nconst isWhitespace = (code: number): boolean =>\n code === 32 ||\n code === 10 ||\n code === 9 ||\n code === 13 ||\n code === 12 ||\n (code >= 128 && reUnicodeSpace.test(String.fromCodePoint(code)))\n\nconst isPunctuation = (code: number): boolean =>\n code < 128 ? isEscapable(code) : reUnicodePunctuation.test(String.fromCodePoint(code))\n\nconst parseNewline = (): void => {\n pos++\n const last = root.last\n if (\n last !== null &&\n last.type === 'text' &&\n last.value.charCodeAt(last.value.length - 1) === 32\n ) {\n const hard = last.value.charCodeAt(last.value.length - 2) === 32\n last.value = last.value.replace(/ +$/, '')\n append(root, inl(hard ? 'break' : 'softbreak'))\n } else append(root, inl('softbreak'))\n while (subject.charCodeAt(pos) === C_SPACE) pos++\n}\n\nconst parseBackslash = (): void => {\n pos++\n const code = subject.charCodeAt(pos)\n if (code === C_NEWLINE) {\n pos++\n append(root, inl('break'))\n while (subject.charCodeAt(pos) === C_SPACE) pos++\n } else if (isEscapable(code)) {\n text(subject.charAt(pos))\n pos++\n } else text('\\\\')\n}\n\nconst parseBackticks = (): void => {\n const start = pos\n while (subject.charCodeAt(pos) === C_BACKTICK) pos++\n const length = pos - start\n const after = pos\n if (noBacktickCloser === null || !noBacktickCloser.has(length)) {\n let from = after\n for (;;) {\n const open = subject.indexOf('`', from)\n if (open === -1) break\n let close = open + 1\n while (subject.charCodeAt(close) === C_BACKTICK) close++\n if (close - open === length) {\n let content = subject.slice(after, open)\n if (content.indexOf('\\n') !== -1) content = content.replace(/\\n/g, ' ')\n if (\n content.charCodeAt(0) === 32 &&\n content.charCodeAt(content.length - 1) === 32 &&\n /[^ ]/.test(content)\n )\n content = content.slice(1, -1)\n append(root, inl('inlineCode', content))\n pos = close\n return\n }\n from = close\n }\n ;(noBacktickCloser ??= new Set()).add(length)\n }\n pos = after\n text(subject.slice(start, after))\n}\n\nconst parseDelimiters = (cc: number): void => {\n const start = pos\n while (subject.charCodeAt(pos) === cc) pos++\n const num = pos - start\n // Strikethrough is one tilde or two; a longer run is text.\n if (cc === C_TILDE && num > 2) {\n text(subject.slice(start, pos))\n return\n }\n let before = C_NEWLINE\n if (start > 0) {\n before = subject.charCodeAt(start - 1)\n if (before >= 0xdc00 && before <= 0xdfff && start > 1)\n before = subject.codePointAt(start - 2) ?? before\n }\n const after = pos < end ? (subject.codePointAt(pos) ?? C_NEWLINE) : C_NEWLINE\n const afterIsWhitespace = isWhitespace(after)\n const afterIsPunctuation = isPunctuation(after)\n const beforeIsWhitespace = isWhitespace(before)\n const beforeIsPunctuation = isPunctuation(before)\n const leftFlanking =\n !afterIsWhitespace && (!afterIsPunctuation || beforeIsWhitespace || beforeIsPunctuation)\n const rightFlanking =\n !beforeIsWhitespace && (!beforeIsPunctuation || afterIsWhitespace || afterIsPunctuation)\n const canOpen =\n cc === C_UNDERSCORE ? leftFlanking && (!rightFlanking || beforeIsPunctuation) : leftFlanking\n const canClose =\n cc === C_UNDERSCORE ? rightFlanking && (!leftFlanking || afterIsPunctuation) : rightFlanking\n const node = text(subject.slice(start, pos))\n if (!canOpen && !canClose) return\n const delimiter: Delimiter = {\n cc,\n num,\n orig: num,\n node,\n prev: delimiters,\n next: null,\n canOpen,\n canClose,\n }\n if (delimiters !== null) delimiters.next = delimiter\n delimiters = delimiter\n}\n\nconst removeDelimiter = (delimiter: Delimiter): void => {\n if (delimiter.prev !== null) delimiter.prev.next = delimiter.next\n if (delimiter.next === null) delimiters = delimiter.prev\n else delimiter.next.prev = delimiter.prev\n}\n\n/** Turns matching delimiter runs above `bottom` into emphasis, strong and strikethrough. */\nconst processEmphasis = (bottom: Delimiter | null): void => {\n // The lowest opener worth looking at, by what kind of closer is asking: `_` and `*` by whether\n // the closer can open and its length modulo three, `~` by its length.\n const bottoms: (Delimiter | null)[] = [\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n ]\n let closer = delimiters\n while (closer !== null && closer.prev !== bottom) closer = closer.prev\n while (closer !== null) {\n if (!closer.canClose) {\n closer = closer.next\n continue\n }\n const cc = closer.cc\n const key =\n cc === C_TILDE\n ? 11 + closer.orig\n : (cc === C_UNDERSCORE ? 0 : 6) + (closer.canOpen ? 3 : 0) + (closer.orig % 3)\n let opener = closer.prev\n let found = false\n while (opener !== null && opener !== bottom && opener !== bottoms[key]) {\n if (opener.cc === cc && opener.canOpen) {\n if (cc === C_TILDE) found = opener.orig === closer.orig\n else\n found = !(\n (closer.canOpen || opener.canClose) &&\n closer.orig % 3 !== 0 &&\n (opener.orig + closer.orig) % 3 === 0\n )\n if (found) break\n }\n opener = opener.prev\n }\n const old: Delimiter = closer\n if (found && opener !== null) {\n const use = cc === C_TILDE ? closer.num : closer.num >= 2 && opener.num >= 2 ? 2 : 1\n opener.num -= use\n closer.num -= use\n opener.node.value = opener.node.value.slice(use)\n closer.node.value = closer.node.value.slice(use)\n const wrapper = inl(cc === C_TILDE ? 'delete' : use === 1 ? 'emphasis' : 'strong')\n let between = opener.node.next\n while (between !== null && between !== closer.node) {\n const next: Inl | null = between.next\n unlink(between)\n append(wrapper, between)\n between = next\n }\n insertAfter(opener.node, wrapper)\n // Delimiters between the two can no longer match anything.\n if (opener.next !== closer) {\n opener.next = closer\n closer.prev = opener\n }\n if (opener.num === 0) {\n unlink(opener.node)\n removeDelimiter(opener)\n }\n if (closer.num === 0) {\n unlink(closer.node)\n const next: Delimiter | null = closer.next\n removeDelimiter(closer)\n closer = next\n }\n } else {\n closer = old.next\n bottoms[key] = old.prev\n if (!old.canOpen) removeDelimiter(old)\n }\n }\n // `removeDelimiter` moves `delimiters` down the stack.\n // oxlint-disable-next-line no-unmodified-loop-condition\n while (delimiters !== null && delimiters !== bottom) removeDelimiter(delimiters)\n}\n\nconst addBracket = (node: Inl, index: number, image: boolean): void => {\n if (brackets !== null) brackets.bracketAfter = true\n brackets = {\n node,\n prev: brackets,\n prevDelimiter: delimiters,\n index,\n image,\n active: true,\n bracketAfter: false,\n }\n}\n\n/** A link destination at the current place, or null. */\nconst parseLinkDestination = (): string | null => {\n const braced = match(reLinkDestinationBraces)\n if (braced !== null) return normalizeUri(unescapeString(braced.slice(1, -1)))\n if (subject.charCodeAt(pos) === C_LESSTHAN) return null\n const start = pos\n let depth = 0\n let code = 0\n while (pos < end) {\n code = subject.charCodeAt(pos)\n if (code === C_BACKSLASH && isEscapable(subject.charCodeAt(pos + 1))) pos += 2\n else if (code === C_OPEN_PAREN) {\n pos++\n depth++\n } else if (code === C_CLOSE_PAREN) {\n if (depth < 1) break\n pos++\n depth--\n } else if (code <= 32 || code === 127) break\n else pos++\n }\n if (pos === start && code !== C_CLOSE_PAREN) return null\n if (depth !== 0) return null\n return normalizeUri(unescapeString(subject.slice(start, pos)))\n}\n\nconst parseLinkTitle = (): string | null => {\n const title = match(reLinkTitle)\n return title === null ? null : unescapeString(title.slice(1, -1))\n}\n\n/** The length of a link label (`[…]`) at the current place, with the place moved past it; or 0. */\nconst parseLinkLabel = (): number => {\n const label = match(reLinkLabel)\n return label === null || label.length > 1001 ? 0 : label.length\n}\n\n/** A reference label as it is matched: without case, with its whitespace collapsed. */\nexport const normalizeLabel = (label: string): string =>\n label\n .slice(1, -1)\n .trim()\n .replace(/[ \\t\\r\\n]+/g, ' ')\n .toLowerCase()\n .toUpperCase()\n\nconst parseCloseBracket = (): void => {\n const start = pos\n pos++\n const opener = brackets\n if (opener === null) {\n text(']')\n return\n }\n if (!opener.active) {\n text(']')\n brackets = opener.prev\n return\n }\n const image = opener.image\n const savepos = pos\n let url = ''\n let title = ''\n let matched = false\n\n // An inline link: `[text](destination \"title\")`.\n if (subject.charCodeAt(pos) === C_OPEN_PAREN) {\n pos++\n spnl()\n const destination = parseLinkDestination()\n if (destination !== null) {\n const beforeTitle = pos\n spnl()\n let found: string | null = null\n // A title needs whitespace before it.\n if (pos !== beforeTitle) found = parseLinkTitle()\n spnl()\n if (subject.charCodeAt(pos) === C_CLOSE_PAREN) {\n pos++\n url = destination\n title = found ?? ''\n matched = true\n }\n }\n if (!matched) pos = savepos\n }\n\n // A reference link: `[text][label]`, `[label][]` or `[label]`.\n if (!matched) {\n const beforeLabel = pos\n const length = parseLinkLabel()\n let label = ''\n if (length > 2) label = subject.slice(beforeLabel, beforeLabel + length)\n else if (!opener.bracketAfter) label = subject.slice(opener.index, start + 1)\n if (length === 0) pos = savepos\n if (label !== '') {\n const reference = ctx.refs.get(normalizeLabel(label))\n if (reference !== undefined) {\n url = reference.url\n title = reference.title\n matched = true\n }\n }\n }\n\n if (!matched) {\n brackets = opener.prev\n pos = start + 1\n text(']')\n return\n }\n\n const node = inl(image ? 'image' : 'link')\n node.url = url\n node.title = title\n let inside = opener.node.next\n while (inside !== null) {\n const next: Inl | null = inside.next\n unlink(inside)\n append(node, inside)\n inside = next\n }\n append(root, node)\n processEmphasis(opener.prevDelimiter)\n brackets = opener.prev\n unlink(opener.node)\n if (!image) {\n // A link cannot hold a link: the brackets opened before this one can no longer open any.\n for (let bracket = brackets; bracket !== null; bracket = bracket.prev)\n if (!bracket.image) bracket.active = false\n }\n}\n\nconst parseOpenBracket = (): void => {\n if (ctx.footnotes.size > 0 && subject.charCodeAt(pos + 1) === C_CARET) {\n reFootnoteReference.lastIndex = pos\n const found = reFootnoteReference.exec(subject)\n const key = found === null ? '' : normalizeLabel(`[${found[1]}]`)\n const label = ctx.footnotes.get(key)\n if (label !== undefined) {\n const uses = (ctx.footnoteUses.get(key) ?? 0) + 1\n ctx.footnoteUses.set(key, uses)\n // A footnote's number is its place among the footnotes referred to so far.\n let index = 0\n for (const used of ctx.footnoteUses.keys()) {\n index++\n if (used === key) break\n }\n state.push({\n type: 'footnoteReference',\n identifier: key.toLowerCase(),\n label,\n index,\n occurrence: uses,\n })\n pos = reFootnoteReference.lastIndex\n return\n }\n }\n const start = pos\n pos++\n addBracket(text('['), start, false)\n}\n\nconst parseBang = (): void => {\n const start = pos\n pos++\n if (\n subject.charCodeAt(pos) === C_OPEN_BRACKET &&\n // `![^1]` is an exclamation mark and a footnote, not an image.\n !(ctx.footnotes.size > 0 && subject.charCodeAt(pos + 1) === C_CARET)\n ) {\n pos++\n addBracket(text('!['), start + 1, true)\n } else text('!')\n}\n\nconst parseLessThan = (): void => {\n reEmailAutolink.lastIndex = pos\n let found = reEmailAutolink.exec(subject)\n if (found !== null) {\n const address = found[1] ?? ''\n const node = inl('link')\n node.url = normalizeUri(`mailto:${address}`)\n append(node, inl('text', address))\n append(root, node)\n pos = reEmailAutolink.lastIndex\n return\n }\n reAutolink.lastIndex = pos\n found = reAutolink.exec(subject)\n if (found !== null) {\n const address = found[1] ?? ''\n const node = inl('link')\n node.url = normalizeUri(address)\n append(node, inl('text', address))\n append(root, node)\n pos = reAutolink.lastIndex\n return\n }\n const tag = match(reHtmlTag)\n if (tag !== null) append(root, inl('html', tag))\n else {\n pos++\n text('<')\n }\n}\n\nconst parseEntity = (): void => {\n reEntity.lastIndex = pos\n const found = reEntity.exec(subject)\n const decoded = found === null ? null : decodeEntity(found[0])\n if (decoded === null) {\n pos++\n text('&')\n } else {\n pos = reEntity.lastIndex\n text(decoded)\n }\n}\n\nconst isAlphanumeric = (code: number): boolean =>\n (code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122)\n\n/** Where a bare URL ends, by GitHub's rules for what trails a link without being part of it. */\nconst trimBareLink = (link: string): string => {\n let length = link.length\n for (;;) {\n const last = link.charCodeAt(length - 1)\n if ('?!.,:*_~\\'\"'.indexOf(link.charAt(length - 1)) !== -1 && length > 0) length--\n else if (last === C_CLOSE_PAREN) {\n let balance = 0\n for (let index = 0; index < length; index++) {\n const code = link.charCodeAt(index)\n if (code === C_OPEN_PAREN) balance++\n else if (code === C_CLOSE_PAREN) balance--\n }\n if (balance >= 0) break\n length--\n } else if (last === 59 /* ; */) {\n let start = length - 2\n while (start >= 0 && isAlphanumeric(link.charCodeAt(start))) start--\n if (start >= 0 && start < length - 2 && link.charCodeAt(start) === C_AMPERSAND) length = start\n else length--\n } else break\n }\n return link.slice(0, length)\n}\n\n/** The bare addresses in a run of text, in order. */\nconst findBareLinks = (run: string): Span[] => {\n const found: Span[] = []\n reBareLink.lastIndex = 0\n for (let hit = reBareLink.exec(run); hit !== null; hit = reBareLink.exec(run)) {\n let start = hit.index\n let link = hit[0]\n const first = link.charCodeAt(0)\n if (link.indexOf('@') !== -1 && !/^(?:https?:|ftp:|www\\.)/.test(link)) {\n let scheme = hit[1] ?? ''\n // `mailto:` and `xmpp:` are part of the link only as a word of their own.\n if (scheme !== '' && start > 0 && isAlphanumeric(run.charCodeAt(start - 1))) {\n start += scheme.length\n link = link.slice(scheme.length)\n scheme = ''\n }\n // Only an XMPP address takes a resource after a slash.\n if (hit[2] !== undefined && scheme !== 'xmpp:') link = link.slice(0, -hit[2].length)\n while (link.charCodeAt(link.length - 1) === 46 /* . */) link = link.slice(0, -1)\n // An address ends in a letter or a digit.\n if (!isAlphanumeric(link.charCodeAt(link.length - 1))) {\n reBareLink.lastIndex = start + link.length\n continue\n }\n found.push({ start, end: start + link.length, url: scheme === '' ? `mailto:${link}` : link })\n reBareLink.lastIndex = start + link.length\n continue\n }\n const www = first === 119 /* w */\n if (start > 0) {\n const before = run.charCodeAt(start - 1)\n // A URL with a scheme starts where its scheme does; `www.` starts a word.\n if (\n www\n ? !isWhitespace(before) &&\n before !== C_ASTERISK &&\n before !== C_UNDERSCORE &&\n before !== C_TILDE &&\n before !== C_OPEN_PAREN\n : (before >= 65 && before <= 90) ||\n (before >= 97 && before <= 122) ||\n before === 60 /* < */\n )\n continue\n }\n const host = www ? 0 : link.indexOf('://') + 3\n let hostEnd = host\n while (hostEnd < link.length) {\n const code = link.charCodeAt(hostEnd)\n if (\n code < 128 &&\n !isAlphanumeric(code) &&\n code !== 45 &&\n code !== 46 &&\n code !== C_UNDERSCORE\n )\n break\n hostEnd++\n }\n const segments = link.slice(host, hostEnd).split('.')\n if (hostEnd === host || (www && (segments.length < 2 || link.length < 5))) continue\n // The last two parts of a domain hold no underscore.\n if (segments.slice(-2).some((segment) => segment.indexOf('_') !== -1)) continue\n link = trimBareLink(link)\n if (link.length <= host) continue\n found.push({ start, end: start + link.length, url: www ? `http://${link}` : link })\n reBareLink.lastIndex = start + link.length\n }\n return found\n}\n\n/** The plain text of a run of nodes: what an image's `alt` holds. */\nconst plainText = (nodes: Node[]): string => {\n let out = ''\n for (const node of nodes) {\n if (node.type === 'image') out += node.alt ?? ''\n else if (node.children) out += plainText(node.children)\n else if (node.type === 'break') out += '\\n'\n else out += node.value ?? ''\n }\n return out\n}\n\n/** A run of finished nodes with the bare addresses in its text made into links. */\nconst linkify = (nodes: Node[]): Node[] => {\n let out: Node[] | null = null\n for (let index = 0; index < nodes.length; index++) {\n const node = nodes[index] as Node\n const value = node.type === 'text' ? (node.value ?? '') : ''\n const found = value !== '' && mayHoldBareLink(value) ? findBareLinks(value) : null\n if (found === null || found.length === 0) {\n if (out !== null) out.push(node)\n continue\n }\n out ??= nodes.slice(0, index)\n let from = 0\n for (const span of found) {\n if (span.start > from) out.push({ type: 'text', value: value.slice(from, span.start) })\n out.push({\n type: 'link',\n url: normalizeUri(span.url),\n title: '',\n children: [{ type: 'text', value: value.slice(span.start, span.end) }],\n })\n from = span.end\n }\n if (from < value.length) out.push({ type: 'text', value: value.slice(from) })\n }\n return out ?? nodes\n}\n\n/**\n * The finished nodes under a parent, with text that ended up side by side joined. `links` is\n * whether bare addresses in the text become links: not inside a link, which cannot hold another.\n */\nconst finish = (parent: Inl, links: boolean): Node[] => {\n const out: Node[] = []\n let last: Node | undefined\n for (let node = parent.first; node !== null; node = node.next) {\n let made: Node\n switch (node.type) {\n case 'text':\n if (node.value === '') continue\n if (last !== undefined && last.type === 'text') {\n last.value += node.value\n continue\n }\n made = { type: 'text', value: node.value }\n break\n case 'link':\n made = { type: 'link', url: node.url, title: node.title, children: finish(node, false) }\n break\n case 'image': {\n const children = finish(node, false)\n made = { type: 'image', url: node.url, title: node.title, alt: plainText(children) }\n break\n }\n case 'emphasis':\n case 'strong':\n case 'delete':\n made = { type: node.type, children: finish(node, links) }\n break\n case 'softbreak':\n // A line ending inside a paragraph is part of its text, as in mdast.\n if (last !== undefined && last.type === 'text') {\n last.value += '\\n'\n continue\n }\n made = { type: 'text', value: '\\n' }\n break\n case 'break':\n made = { type: 'break' }\n break\n case 'node':\n if (node.node === null) continue\n made = node.node\n break\n default:\n made = { type: node.type, value: node.value }\n }\n out.push(made)\n last = made\n }\n return links ? linkify(out) : out\n}\n\n/**\n * Runs one parse of a document and puts back what the inline parser was reading before it. The\n * parser keeps its place in this module, which is what makes it fast; an extension's rule that\n * parses a text of its own (a caption, a cell) starts a second parse in the middle of the first,\n * and the first one goes on from where it was.\n */\nexport const isolated = <T>(run: () => T): T => {\n const held = [subject, pos, end, ctx, root, delimiters, brackets, bareLinks, noBacktickCloser]\n const heldText = state.text\n const heldPos = state.pos\n try {\n return run()\n } finally {\n ;[subject, pos, end, ctx, root, delimiters, brackets, bareLinks, noBacktickCloser] = held as [\n string,\n number,\n number,\n InlineContext,\n Inl,\n typeof delimiters,\n typeof brackets,\n boolean,\n typeof noBacktickCloser,\n ]\n state.text = heldText\n state.pos = heldPos\n }\n}\n\nconst state: InlineState = {\n text: '',\n pos: 0,\n push: (node) => {\n const wrapper = inl('node')\n wrapper.node = node\n append(root, wrapper)\n },\n}\n\n/** Reads the inline text of one block into nodes. */\nexport const parseInlines = (source: string, context: InlineContext): Node[] => {\n // The text of a block ends where its last line does: trailing whitespace is not content.\n let length = source.length\n while (length > 0) {\n const code = source.charCodeAt(length - 1)\n if (code !== 32 && code !== 10 && code !== 9) break\n length--\n }\n subject = length === source.length ? source : source.slice(0, length)\n pos = 0\n end = length\n ctx = context\n root = inl('root')\n delimiters = null\n brackets = null\n noBacktickCloser = null\n bareLinks = context.gfm && mayHoldBareLink(subject)\n const special = context.special\n const rules = context.rules\n\n while (pos < end) {\n const code = subject.charCodeAt(pos)\n const own = code < 128 ? rules[code] : undefined\n if (own !== undefined) {\n state.text = subject\n state.pos = pos\n let handled = false\n for (const rule of own) {\n if (rule(state)) {\n handled = true\n break\n }\n }\n if (handled) {\n pos = state.pos\n continue\n }\n }\n switch (code) {\n case C_NEWLINE:\n parseNewline()\n break\n case C_BACKSLASH:\n parseBackslash()\n break\n case C_BACKTICK:\n parseBackticks()\n break\n case C_ASTERISK:\n case C_UNDERSCORE:\n parseDelimiters(code)\n break\n case C_TILDE:\n if (context.gfm) parseDelimiters(code)\n else {\n pos++\n text('~')\n }\n break\n case C_OPEN_BRACKET:\n parseOpenBracket()\n break\n case C_BANG:\n parseBang()\n break\n case C_CLOSE_BRACKET:\n parseCloseBracket()\n break\n case C_LESSTHAN:\n parseLessThan()\n break\n case C_AMPERSAND:\n parseEntity()\n break\n default: {\n // A run of plain text, up to the next character that can start something.\n const start = pos\n pos++\n while (pos < end) {\n const next = subject.charCodeAt(pos)\n if (next < 128 && special[next] === 1) break\n pos++\n }\n text(subject.slice(start, pos))\n }\n }\n }\n processEmphasis(null)\n const nodes = finish(root, bareLinks)\n subject = ''\n return nodes\n}\n\n/**\n * Reads one link reference definition from the start of a paragraph's text into `refs`. Returns\n * how many characters it took, or 0 when the text does not start with a definition.\n */\nexport const parseReference = (source: string, refs: Map<string, Reference>): number => {\n subject = source\n pos = 0\n end = source.length\n const labelLength = parseLinkLabel()\n if (labelLength === 0) return 0\n const label = source.slice(0, labelLength)\n if (source.charCodeAt(pos) !== C_COLON) return 0\n pos++\n spnl()\n const destination = parseLinkDestination()\n if (destination === null) return 0\n const beforeTitle = pos\n spnl()\n let title: string | null = null\n if (pos !== beforeTitle) title = parseLinkTitle()\n if (title === null) pos = beforeTitle\n let atLineEnd = match(reSpaceAtEndOfLine) !== null\n if (!atLineEnd && title !== null) {\n // What follows the title is not the end of the line: the definition is the part before it.\n title = null\n pos = beforeTitle\n atLineEnd = match(reSpaceAtEndOfLine) !== null\n }\n if (!atLineEnd) return 0\n const normalized = normalizeLabel(label)\n if (normalized === '') return 0\n if (!refs.has(normalized)) refs.set(normalized, { url: destination, title: title ?? '' })\n return pos\n}\n\n/** The characters that start an inline construct, as a table by ASCII code. */\nexport const specialCharacters = (extra: string): Uint8Array => {\n const table = new Uint8Array(128)\n for (const char of `\\n\\\\\\`*_~[]!<&${extra}`) table[char.charCodeAt(0)] = 1\n return table\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"inlines.js","names":[],"sources":["../../src/engine/inlines.ts"],"sourcesContent":["/**\n * The inline pass: the text of one block read into emphasis, links, code spans and the rest, by\n * the algorithm in the CommonMark spec (a delimiter stack for emphasis, a bracket stack for\n * links). One pass over the text; the parser's state lives in the module, since a block's text\n * is read from start to end before the next one begins.\n */\nimport { decodeEntity, ENTITY, isEscapable, normalizeUri, unescapeString } from './entities'\nimport type { InlineRule, InlineState, Node, Reference } from './types'\nimport { toText } from './visit'\n\nexport interface InlineContext {\n refs: Map<string, Reference>\n /** The footnotes the document defines: normalized label to the label as written. */\n footnotes: Map<string, string>\n /** How many times each footnote has been referred to so far, in the order first referred to. */\n footnoteUses: Map<string, number>\n gfm: boolean\n /** Whether `$$` alone on a line fences a display formula. */\n math: boolean\n /** Extension rules by the ASCII code of the character that starts them. */\n rules: (InlineRule['parse'][] | undefined)[]\n /** 1 for every ASCII character that can start something other than plain text. */\n special: Uint8Array\n}\n\n/** A node while its block is read: linked to its siblings, so emphasis can wrap a run of them. */\ninterface Inl {\n type: string\n value: string\n parent: Inl | null\n prev: Inl | null\n next: Inl | null\n first: Inl | null\n last: Inl | null\n url: string\n title: string\n /** The finished node of an extension's construct. */\n node: Node | null\n}\n\ninterface Delimiter {\n cc: number\n num: number\n orig: number\n node: Inl\n prev: Delimiter | null\n next: Delimiter | null\n canOpen: boolean\n canClose: boolean\n}\n\ninterface Bracket {\n node: Inl\n prev: Bracket | null\n prevDelimiter: Delimiter | null\n index: number\n image: boolean\n active: boolean\n bracketAfter: boolean\n}\n\n/** A bare address found in the text: where it is and where it leads. */\ninterface Span {\n start: number\n end: number\n url: string\n}\n\nconst C_NEWLINE = 10\nconst C_SPACE = 32\nconst C_BANG = 33\nconst C_AMPERSAND = 38\nconst C_OPEN_PAREN = 40\nconst C_CLOSE_PAREN = 41\nconst C_ASTERISK = 42\nconst C_COLON = 58\nconst C_LESSTHAN = 60\nconst C_OPEN_BRACKET = 91\nconst C_BACKSLASH = 92\nconst C_CLOSE_BRACKET = 93\nconst C_CARET = 94\nconst C_UNDERSCORE = 95\nconst C_BACKTICK = 96\nconst C_TILDE = 126\n\nconst TAGNAME = '[A-Za-z][A-Za-z0-9-]*'\nconst ATTRIBUTENAME = '[a-zA-Z_:][a-zA-Z0-9:._-]*'\nconst ATTRIBUTEVALUE = '(?:[^\"\\'=<>`\\\\x00-\\\\x20]+|\\'[^\\']*\\'|\"[^\"]*\")'\nconst ATTRIBUTE = `(?:\\\\s+${ATTRIBUTENAME}(?:\\\\s*=\\\\s*${ATTRIBUTEVALUE})?)`\nexport const OPENTAG = `<${TAGNAME}${ATTRIBUTE}*\\\\s*/?>`\nexport const CLOSETAG = `</${TAGNAME}\\\\s*[>]`\nconst HTMLTAG = `(?:${OPENTAG}|${CLOSETAG}|<!-->|<!--->|<!--[\\\\s\\\\S]*?-->|[<][?][\\\\s\\\\S]*?[?][>]|<![A-Za-z]+[^>]*>|<!\\\\[CDATA\\\\[[\\\\s\\\\S]*?\\\\]\\\\]>)`\n\nconst reHtmlTag = new RegExp(HTMLTAG, 'y')\nconst reEntity = new RegExp(ENTITY, 'iy')\n// The specification's own character classes name control characters.\n// oxlint-disable-next-line no-control-regex\nconst reAutolink = /<([A-Za-z][A-Za-z0-9.+-]{1,31}:[^<>\\x00-\\x20]*)>/y\nconst reEmailAutolink =\n /<([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/y\nconst reLinkLabel = /\\[(?:[^\\\\[\\]]|\\\\[\\s\\S]){0,1000}\\]/y\n// oxlint-disable-next-line no-control-regex\nconst reLinkDestinationBraces = /<(?:[^<>\\n\\\\\\x00]|\\\\.)*>/y\nconst reLinkTitle = /\"(?:\\\\[\\s\\S]|[^\"\\\\])*\"|'(?:\\\\[\\s\\S]|[^'\\\\])*'|\\((?:\\\\[\\s\\S]|[^()\\\\])*\\)/y\nconst reSpaceAtEndOfLine = /[ \\t]*(?:\\n|$)/y\nconst reFootnoteReference = /\\[\\^([^\\]\\s]+)\\]/y\nconst reUnicodeSpace = /\\p{Zs}/u\nconst reUnicodePunctuation = /[\\p{P}\\p{S}]/u\n/** Bare addresses: a URL with a scheme, one that starts `www.`, or an email address. */\nconst reBareLink =\n /(?:(?:https?|ftp):\\/\\/|www\\.)[^\\s<]*|(mailto:|xmpp:)?[A-Za-z0-9._+-]+@[A-Za-z0-9_-]+(?:\\.[A-Za-z0-9_-]+)+(\\/[A-Za-z0-9@.]+)?/g\n\n/** Whether the text may hold a bare address: three searches for a few characters, no regex. */\nconst mayHoldBareLink = (text: string): boolean =>\n text.indexOf('@') !== -1 || text.indexOf('://') !== -1 || text.indexOf('www.') !== -1\n\nlet subject = ''\nlet pos = 0\nlet end = 0\nlet ctx: InlineContext\nlet root: Inl\nlet delimiters: Delimiter | null = null\nlet brackets: Bracket | null = null\n/** Whether the text may hold a bare address (`www.`, `://`, `@`) to turn into a link. */\nlet bareLinks = false\n/** Lengths of backtick runs known to have no closing run in the rest of the text. */\nlet noBacktickCloser: Set<number> | null = null\n\nconst inl = (type: string, value = ''): Inl => ({\n type,\n value,\n parent: null,\n prev: null,\n next: null,\n first: null,\n last: null,\n url: '',\n title: '',\n node: null,\n})\n\nconst append = (parent: Inl, child: Inl): void => {\n child.parent = parent\n child.prev = parent.last\n child.next = null\n if (parent.last) parent.last.next = child\n else parent.first = child\n parent.last = child\n}\n\nconst unlink = (node: Inl): void => {\n if (node.prev) node.prev.next = node.next\n else if (node.parent) node.parent.first = node.next\n if (node.next) node.next.prev = node.prev\n else if (node.parent) node.parent.last = node.prev\n node.parent = node.prev = node.next = null\n}\n\nconst insertAfter = (node: Inl, sibling: Inl): void => {\n sibling.parent = node.parent\n sibling.prev = node\n sibling.next = node.next\n if (node.next) node.next.prev = sibling\n else if (node.parent) node.parent.last = sibling\n node.next = sibling\n}\n\nconst text = (value: string): Inl => {\n const node = inl('text', value)\n append(root, node)\n return node\n}\n\n/** Runs a sticky expression at the current place; the match, with the place moved past it. */\nconst match = (re: RegExp): string | null => {\n re.lastIndex = pos\n const found = re.exec(subject)\n if (found === null) return null\n pos = re.lastIndex\n return found[0]\n}\n\n/** Skips spaces, tabs and at most one line ending. */\nconst spnl = (): void => {\n let code = subject.charCodeAt(pos)\n while (code === C_SPACE || code === 9) code = subject.charCodeAt(++pos)\n if (code === C_NEWLINE) {\n code = subject.charCodeAt(++pos)\n while (code === C_SPACE || code === 9) code = subject.charCodeAt(++pos)\n }\n}\n\nconst isWhitespace = (code: number): boolean =>\n code === 32 ||\n code === 10 ||\n code === 9 ||\n code === 13 ||\n code === 12 ||\n (code >= 128 && reUnicodeSpace.test(String.fromCodePoint(code)))\n\nconst isPunctuation = (code: number): boolean =>\n code < 128 ? isEscapable(code) : reUnicodePunctuation.test(String.fromCodePoint(code))\n\nconst parseNewline = (): void => {\n pos++\n const last = root.last\n if (\n last !== null &&\n last.type === 'text' &&\n last.value.charCodeAt(last.value.length - 1) === 32\n ) {\n const hard = last.value.charCodeAt(last.value.length - 2) === 32\n last.value = last.value.replace(/ +$/, '')\n append(root, inl(hard ? 'break' : 'softbreak'))\n } else append(root, inl('softbreak'))\n while (subject.charCodeAt(pos) === C_SPACE) pos++\n}\n\nconst parseBackslash = (): void => {\n pos++\n const code = subject.charCodeAt(pos)\n if (code === C_NEWLINE) {\n pos++\n append(root, inl('break'))\n while (subject.charCodeAt(pos) === C_SPACE) pos++\n } else if (isEscapable(code)) {\n text(subject.charAt(pos))\n pos++\n } else text('\\\\')\n}\n\nconst parseBackticks = (): void => {\n const start = pos\n while (subject.charCodeAt(pos) === C_BACKTICK) pos++\n const length = pos - start\n const after = pos\n if (noBacktickCloser === null || !noBacktickCloser.has(length)) {\n let from = after\n for (;;) {\n const open = subject.indexOf('`', from)\n if (open === -1) break\n let close = open + 1\n while (subject.charCodeAt(close) === C_BACKTICK) close++\n if (close - open === length) {\n let content = subject.slice(after, open)\n if (content.indexOf('\\n') !== -1) content = content.replace(/\\n/g, ' ')\n if (\n content.charCodeAt(0) === 32 &&\n content.charCodeAt(content.length - 1) === 32 &&\n /[^ ]/.test(content)\n )\n content = content.slice(1, -1)\n append(root, inl('inlineCode', content))\n pos = close\n return\n }\n from = close\n }\n ;(noBacktickCloser ??= new Set()).add(length)\n }\n pos = after\n text(subject.slice(start, after))\n}\n\nconst parseDelimiters = (cc: number): void => {\n const start = pos\n while (subject.charCodeAt(pos) === cc) pos++\n const num = pos - start\n // Strikethrough is one tilde or two; a longer run is text.\n if (cc === C_TILDE && num > 2) {\n text(subject.slice(start, pos))\n return\n }\n let before = C_NEWLINE\n if (start > 0) {\n before = subject.charCodeAt(start - 1)\n if (before >= 0xdc00 && before <= 0xdfff && start > 1)\n before = subject.codePointAt(start - 2) ?? before\n }\n const after = pos < end ? (subject.codePointAt(pos) ?? C_NEWLINE) : C_NEWLINE\n const afterIsWhitespace = isWhitespace(after)\n const afterIsPunctuation = isPunctuation(after)\n const beforeIsWhitespace = isWhitespace(before)\n const beforeIsPunctuation = isPunctuation(before)\n const leftFlanking =\n !afterIsWhitespace && (!afterIsPunctuation || beforeIsWhitespace || beforeIsPunctuation)\n const rightFlanking =\n !beforeIsWhitespace && (!beforeIsPunctuation || afterIsWhitespace || afterIsPunctuation)\n const canOpen =\n cc === C_UNDERSCORE ? leftFlanking && (!rightFlanking || beforeIsPunctuation) : leftFlanking\n const canClose =\n cc === C_UNDERSCORE ? rightFlanking && (!leftFlanking || afterIsPunctuation) : rightFlanking\n const node = text(subject.slice(start, pos))\n if (!canOpen && !canClose) return\n const delimiter: Delimiter = {\n cc,\n num,\n orig: num,\n node,\n prev: delimiters,\n next: null,\n canOpen,\n canClose,\n }\n if (delimiters !== null) delimiters.next = delimiter\n delimiters = delimiter\n}\n\nconst removeDelimiter = (delimiter: Delimiter): void => {\n if (delimiter.prev !== null) delimiter.prev.next = delimiter.next\n if (delimiter.next === null) delimiters = delimiter.prev\n else delimiter.next.prev = delimiter.prev\n}\n\n/** Turns matching delimiter runs above `bottom` into emphasis, strong and strikethrough. */\nconst processEmphasis = (bottom: Delimiter | null): void => {\n // The lowest opener worth looking at, by what kind of closer is asking: `_` and `*` by whether\n // the closer can open and its length modulo three, `~` by its length.\n const bottoms: (Delimiter | null)[] = [\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n bottom,\n ]\n let closer = delimiters\n while (closer !== null && closer.prev !== bottom) closer = closer.prev\n while (closer !== null) {\n if (!closer.canClose) {\n closer = closer.next\n continue\n }\n const cc = closer.cc\n const key =\n cc === C_TILDE\n ? 11 + closer.orig\n : (cc === C_UNDERSCORE ? 0 : 6) + (closer.canOpen ? 3 : 0) + (closer.orig % 3)\n let opener = closer.prev\n let found = false\n while (opener !== null && opener !== bottom && opener !== bottoms[key]) {\n if (opener.cc === cc && opener.canOpen) {\n if (cc === C_TILDE) found = opener.orig === closer.orig\n else\n found = !(\n (closer.canOpen || opener.canClose) &&\n closer.orig % 3 !== 0 &&\n (opener.orig + closer.orig) % 3 === 0\n )\n if (found) break\n }\n opener = opener.prev\n }\n const old: Delimiter = closer\n if (found && opener !== null) {\n const use = cc === C_TILDE ? closer.num : closer.num >= 2 && opener.num >= 2 ? 2 : 1\n opener.num -= use\n closer.num -= use\n opener.node.value = opener.node.value.slice(use)\n closer.node.value = closer.node.value.slice(use)\n const wrapper = inl(cc === C_TILDE ? 'delete' : use === 1 ? 'emphasis' : 'strong')\n let between = opener.node.next\n while (between !== null && between !== closer.node) {\n const next: Inl | null = between.next\n unlink(between)\n append(wrapper, between)\n between = next\n }\n insertAfter(opener.node, wrapper)\n // Delimiters between the two can no longer match anything.\n if (opener.next !== closer) {\n opener.next = closer\n closer.prev = opener\n }\n if (opener.num === 0) {\n unlink(opener.node)\n removeDelimiter(opener)\n }\n if (closer.num === 0) {\n unlink(closer.node)\n const next: Delimiter | null = closer.next\n removeDelimiter(closer)\n closer = next\n }\n } else {\n closer = old.next\n bottoms[key] = old.prev\n if (!old.canOpen) removeDelimiter(old)\n }\n }\n // `removeDelimiter` moves `delimiters` down the stack.\n // oxlint-disable-next-line no-unmodified-loop-condition\n while (delimiters !== null && delimiters !== bottom) removeDelimiter(delimiters)\n}\n\nconst addBracket = (node: Inl, index: number, image: boolean): void => {\n if (brackets !== null) brackets.bracketAfter = true\n brackets = {\n node,\n prev: brackets,\n prevDelimiter: delimiters,\n index,\n image,\n active: true,\n bracketAfter: false,\n }\n}\n\n/** How deep the parentheses of a link destination may nest. */\nconst MAX_PARENS = 32\n\n/** A link destination at the current place, or null. */\nconst parseLinkDestination = (): string | null => {\n const braced = match(reLinkDestinationBraces)\n if (braced !== null) return normalizeUri(unescapeString(braced.slice(1, -1)))\n if (subject.charCodeAt(pos) === C_LESSTHAN) return null\n const start = pos\n let depth = 0\n let code = 0\n while (pos < end) {\n code = subject.charCodeAt(pos)\n if (code === C_BACKSLASH && isEscapable(subject.charCodeAt(pos + 1))) pos += 2\n else if (code === C_OPEN_PAREN) {\n pos++\n // As cmark does: without a limit, `[a](` written n times is read n times to its end.\n if (++depth > MAX_PARENS) return null\n } else if (code === C_CLOSE_PAREN) {\n if (depth < 1) break\n pos++\n depth--\n } else if (code <= 32 || code === 127) break\n else pos++\n }\n if (pos === start && code !== C_CLOSE_PAREN) return null\n if (depth !== 0) return null\n return normalizeUri(unescapeString(subject.slice(start, pos)))\n}\n\nconst parseLinkTitle = (): string | null => {\n const title = match(reLinkTitle)\n return title === null ? null : unescapeString(title.slice(1, -1))\n}\n\n/** The length of a link label (`[…]`) at the current place, with the place moved past it; or 0. */\nconst parseLinkLabel = (): number => {\n const label = match(reLinkLabel)\n return label === null || label.length > 1001 ? 0 : label.length\n}\n\n/** A reference label as it is matched: without case, with its whitespace collapsed. */\nexport const normalizeLabel = (label: string): string =>\n label\n .slice(1, -1)\n .trim()\n .replace(/[ \\t\\r\\n]+/g, ' ')\n .toLowerCase()\n .toUpperCase()\n\nconst parseCloseBracket = (): void => {\n const start = pos\n pos++\n const opener = brackets\n if (opener === null) {\n text(']')\n return\n }\n if (!opener.active) {\n text(']')\n brackets = opener.prev\n return\n }\n const image = opener.image\n const savepos = pos\n let url = ''\n let title = ''\n let matched = false\n\n // An inline link: `[text](destination \"title\")`.\n if (subject.charCodeAt(pos) === C_OPEN_PAREN) {\n pos++\n spnl()\n const destination = parseLinkDestination()\n if (destination !== null) {\n const beforeTitle = pos\n spnl()\n let found: string | null = null\n // A title needs whitespace before it.\n if (pos !== beforeTitle) found = parseLinkTitle()\n spnl()\n if (subject.charCodeAt(pos) === C_CLOSE_PAREN) {\n pos++\n url = destination\n title = found ?? ''\n matched = true\n }\n }\n if (!matched) pos = savepos\n }\n\n // A reference link: `[text][label]`, `[label][]` or `[label]`.\n if (!matched) {\n const beforeLabel = pos\n const length = parseLinkLabel()\n let label = ''\n if (length > 2) label = subject.slice(beforeLabel, beforeLabel + length)\n else if (!opener.bracketAfter) label = subject.slice(opener.index, start + 1)\n if (length === 0) pos = savepos\n if (label !== '') {\n const reference = ctx.refs.get(normalizeLabel(label))\n if (reference !== undefined) {\n url = reference.url\n title = reference.title\n matched = true\n }\n }\n }\n\n if (!matched) {\n brackets = opener.prev\n pos = start + 1\n text(']')\n return\n }\n\n const node = inl(image ? 'image' : 'link')\n node.url = url\n node.title = title\n let inside = opener.node.next\n while (inside !== null) {\n const next: Inl | null = inside.next\n unlink(inside)\n append(node, inside)\n inside = next\n }\n append(root, node)\n processEmphasis(opener.prevDelimiter)\n brackets = opener.prev\n unlink(opener.node)\n if (!image) {\n // A link cannot hold a link: the brackets opened before this one can no longer open any.\n for (let bracket = brackets; bracket !== null; bracket = bracket.prev)\n if (!bracket.image) bracket.active = false\n }\n}\n\nconst parseOpenBracket = (): void => {\n if (ctx.footnotes.size > 0 && subject.charCodeAt(pos + 1) === C_CARET) {\n reFootnoteReference.lastIndex = pos\n const found = reFootnoteReference.exec(subject)\n const key = found === null ? '' : normalizeLabel(`[${found[1]}]`)\n const label = ctx.footnotes.get(key)\n if (label !== undefined) {\n const uses = (ctx.footnoteUses.get(key) ?? 0) + 1\n ctx.footnoteUses.set(key, uses)\n // A footnote's number is its place among the footnotes referred to so far.\n let index = 0\n for (const used of ctx.footnoteUses.keys()) {\n index++\n if (used === key) break\n }\n state.push({\n type: 'footnoteReference',\n identifier: key.toLowerCase(),\n label,\n index,\n occurrence: uses,\n })\n pos = reFootnoteReference.lastIndex\n return\n }\n }\n const start = pos\n pos++\n addBracket(text('['), start, false)\n}\n\nconst parseBang = (): void => {\n const start = pos\n pos++\n if (\n subject.charCodeAt(pos) === C_OPEN_BRACKET &&\n // `![^1]` is an exclamation mark and a footnote, not an image.\n !(ctx.footnotes.size > 0 && subject.charCodeAt(pos + 1) === C_CARET)\n ) {\n pos++\n addBracket(text('!['), start + 1, true)\n } else text('!')\n}\n\nconst parseLessThan = (): void => {\n reEmailAutolink.lastIndex = pos\n let found = reEmailAutolink.exec(subject)\n if (found !== null) {\n const address = found[1] ?? ''\n const node = inl('link')\n node.url = normalizeUri(`mailto:${address}`)\n append(node, inl('text', address))\n append(root, node)\n pos = reEmailAutolink.lastIndex\n return\n }\n reAutolink.lastIndex = pos\n found = reAutolink.exec(subject)\n if (found !== null) {\n const address = found[1] ?? ''\n const node = inl('link')\n node.url = normalizeUri(address)\n append(node, inl('text', address))\n append(root, node)\n pos = reAutolink.lastIndex\n return\n }\n const tag = match(reHtmlTag)\n if (tag !== null) append(root, inl('html', tag))\n else {\n pos++\n text('<')\n }\n}\n\nconst parseEntity = (): void => {\n reEntity.lastIndex = pos\n const found = reEntity.exec(subject)\n const decoded = found === null ? null : decodeEntity(found[0])\n if (decoded === null) {\n pos++\n text('&')\n } else {\n pos = reEntity.lastIndex\n text(decoded)\n }\n}\n\nconst isAlphanumeric = (code: number): boolean =>\n (code >= 48 && code <= 57) || (code >= 65 && code <= 90) || (code >= 97 && code <= 122)\n\n/** Where a bare URL ends, by GitHub's rules for what trails a link without being part of it. */\nconst trimBareLink = (link: string): string => {\n let length = link.length\n for (;;) {\n const last = link.charCodeAt(length - 1)\n if ('?!.,:*_~\\'\"'.indexOf(link.charAt(length - 1)) !== -1 && length > 0) length--\n else if (last === C_CLOSE_PAREN) {\n let balance = 0\n for (let index = 0; index < length; index++) {\n const code = link.charCodeAt(index)\n if (code === C_OPEN_PAREN) balance++\n else if (code === C_CLOSE_PAREN) balance--\n }\n if (balance >= 0) break\n length--\n } else if (last === 59 /* ; */) {\n let start = length - 2\n while (start >= 0 && isAlphanumeric(link.charCodeAt(start))) start--\n if (start >= 0 && start < length - 2 && link.charCodeAt(start) === C_AMPERSAND) length = start\n else length--\n } else break\n }\n return link.slice(0, length)\n}\n\n/** The bare addresses in a run of text, in order. */\nconst findBareLinks = (run: string): Span[] => {\n const found: Span[] = []\n reBareLink.lastIndex = 0\n for (let hit = reBareLink.exec(run); hit !== null; hit = reBareLink.exec(run)) {\n let start = hit.index\n let link = hit[0]\n const first = link.charCodeAt(0)\n if (link.indexOf('@') !== -1 && !/^(?:https?:|ftp:|www\\.)/.test(link)) {\n let scheme = hit[1] ?? ''\n // `mailto:` and `xmpp:` are part of the link only as a word of their own.\n if (scheme !== '' && start > 0 && isAlphanumeric(run.charCodeAt(start - 1))) {\n start += scheme.length\n link = link.slice(scheme.length)\n scheme = ''\n }\n // Only an XMPP address takes a resource after a slash.\n if (hit[2] !== undefined && scheme !== 'xmpp:') link = link.slice(0, -hit[2].length)\n while (link.charCodeAt(link.length - 1) === 46 /* . */) link = link.slice(0, -1)\n // An address ends in a letter or a digit.\n if (!isAlphanumeric(link.charCodeAt(link.length - 1))) {\n reBareLink.lastIndex = start + link.length\n continue\n }\n found.push({ start, end: start + link.length, url: scheme === '' ? `mailto:${link}` : link })\n reBareLink.lastIndex = start + link.length\n continue\n }\n const www = first === 119 /* w */\n if (start > 0) {\n const before = run.charCodeAt(start - 1)\n // A URL with a scheme starts where its scheme does; `www.` starts a word.\n if (\n www\n ? !isWhitespace(before) &&\n before !== C_ASTERISK &&\n before !== C_UNDERSCORE &&\n before !== C_TILDE &&\n before !== C_OPEN_PAREN\n : (before >= 65 && before <= 90) ||\n (before >= 97 && before <= 122) ||\n before === 60 /* < */\n )\n continue\n }\n const host = www ? 0 : link.indexOf('://') + 3\n let hostEnd = host\n while (hostEnd < link.length) {\n const code = link.charCodeAt(hostEnd)\n if (\n code < 128 &&\n !isAlphanumeric(code) &&\n code !== 45 &&\n code !== 46 &&\n code !== C_UNDERSCORE\n )\n break\n hostEnd++\n }\n const segments = link.slice(host, hostEnd).split('.')\n if (hostEnd === host || (www && (segments.length < 2 || link.length < 5))) continue\n // The last two parts of a domain hold no underscore.\n if (segments.slice(-2).some((segment) => segment.indexOf('_') !== -1)) continue\n link = trimBareLink(link)\n if (link.length <= host) continue\n found.push({ start, end: start + link.length, url: www ? `http://${link}` : link })\n reBareLink.lastIndex = start + link.length\n }\n return found\n}\n\n/** The plain text of a run of nodes: what an image's `alt` holds. */\nconst plainText = (nodes: Node[]): string => {\n let out = ''\n for (const node of nodes) {\n if (node.type === 'image') out += node.alt ?? ''\n else if (node.children) out += plainText(node.children)\n else if (node.type === 'break') out += '\\n'\n else out += node.value ?? ''\n }\n return out\n}\n\n/** A run of finished nodes with the bare addresses in its text made into links. */\nconst linkify = (nodes: Node[]): Node[] => {\n let out: Node[] | null = null\n for (let index = 0; index < nodes.length; index++) {\n const node = nodes[index] as Node\n const value = node.type === 'text' ? (node.value ?? '') : ''\n const found = value !== '' && mayHoldBareLink(value) ? findBareLinks(value) : null\n if (found === null || found.length === 0) {\n if (out !== null) out.push(node)\n continue\n }\n out ??= nodes.slice(0, index)\n let from = 0\n for (const span of found) {\n if (span.start > from) out.push({ type: 'text', value: value.slice(from, span.start) })\n out.push({\n type: 'link',\n url: normalizeUri(span.url),\n title: '',\n children: [{ type: 'text', value: value.slice(span.start, span.end) }],\n })\n from = span.end\n }\n if (from < value.length) out.push({ type: 'text', value: value.slice(from) })\n }\n return out ?? nodes\n}\n\n/**\n * How deep emphasis, links and images may nest. Thousands of `*` around a word are thousands of\n * nodes one inside another, and every pass after this one walks the tree by calling itself: past\n * the limit the rest is kept as its text.\n */\nconst MAX_NESTING = 100\n\n/** The text under `parent`, read without a call per level. */\nconst flat = (parent: Inl): string => {\n let out = ''\n const open: Inl[] = []\n let node = parent.first\n for (;;) {\n if (node === null) {\n const up = open.pop()\n if (up === undefined) return out\n node = up.next\n } else if (node.first !== null) {\n open.push(node)\n node = node.first\n } else {\n if (node.type === 'softbreak' || node.type === 'break') out += '\\n'\n else if (node.type === 'node') out += node.node === null ? '' : toText(node.node)\n else out += node.value\n node = node.next\n }\n }\n}\n\n/**\n * The finished nodes under a parent, with text that ended up side by side joined. `links` is\n * whether bare addresses in the text become links: not inside a link, which cannot hold another.\n */\nconst finish = (parent: Inl, links: boolean, depth = 0): Node[] => {\n if (depth >= MAX_NESTING) {\n const value = flat(parent)\n return value === '' ? [] : [{ type: 'text', value }]\n }\n const out: Node[] = []\n let last: Node | undefined\n for (let node = parent.first; node !== null; node = node.next) {\n let made: Node\n switch (node.type) {\n case 'text':\n if (node.value === '') continue\n if (last !== undefined && last.type === 'text') {\n last.value += node.value\n continue\n }\n made = { type: 'text', value: node.value }\n break\n case 'link':\n made = {\n type: 'link',\n url: node.url,\n title: node.title,\n children: finish(node, false, depth + 1),\n }\n break\n case 'image': {\n const children = finish(node, false, depth + 1)\n made = { type: 'image', url: node.url, title: node.title, alt: plainText(children) }\n break\n }\n case 'emphasis':\n case 'strong':\n case 'delete':\n made = { type: node.type, children: finish(node, links, depth + 1) }\n break\n case 'softbreak':\n // A line ending inside a paragraph is part of its text, as in mdast.\n if (last !== undefined && last.type === 'text') {\n last.value += '\\n'\n continue\n }\n made = { type: 'text', value: '\\n' }\n break\n case 'break':\n made = { type: 'break' }\n break\n case 'node':\n if (node.node === null) continue\n made = node.node\n break\n default:\n made = { type: node.type, value: node.value }\n }\n out.push(made)\n last = made\n }\n return links ? linkify(out) : out\n}\n\n/**\n * Runs one parse of a document and puts back what the inline parser was reading before it. The\n * parser keeps its place in this module, which is what makes it fast; an extension's rule that\n * parses a text of its own (a caption, a cell) starts a second parse in the middle of the first,\n * and the first one goes on from where it was.\n */\nexport const isolated = <T>(run: () => T): T => {\n const held = [subject, pos, end, ctx, root, delimiters, brackets, bareLinks, noBacktickCloser]\n const heldText = state.text\n const heldPos = state.pos\n try {\n return run()\n } finally {\n ;[subject, pos, end, ctx, root, delimiters, brackets, bareLinks, noBacktickCloser] = held as [\n string,\n number,\n number,\n InlineContext,\n Inl,\n typeof delimiters,\n typeof brackets,\n boolean,\n typeof noBacktickCloser,\n ]\n state.text = heldText\n state.pos = heldPos\n }\n}\n\nconst state: InlineState = {\n text: '',\n pos: 0,\n push: (node) => {\n const wrapper = inl('node')\n wrapper.node = node\n append(root, wrapper)\n },\n}\n\n/** Reads the inline text of one block into nodes. */\nexport const parseInlines = (source: string, context: InlineContext): Node[] => {\n // The text of a block ends where its last line does: trailing whitespace is not content.\n let length = source.length\n while (length > 0) {\n const code = source.charCodeAt(length - 1)\n if (code !== 32 && code !== 10 && code !== 9) break\n length--\n }\n subject = length === source.length ? source : source.slice(0, length)\n pos = 0\n end = length\n ctx = context\n root = inl('root')\n delimiters = null\n brackets = null\n noBacktickCloser = null\n bareLinks = context.gfm && mayHoldBareLink(subject)\n const special = context.special\n const rules = context.rules\n\n while (pos < end) {\n const code = subject.charCodeAt(pos)\n const own = code < 128 ? rules[code] : undefined\n if (own !== undefined) {\n state.text = subject\n state.pos = pos\n let handled = false\n for (const rule of own) {\n if (rule(state)) {\n handled = true\n break\n }\n }\n if (handled) {\n pos = state.pos\n continue\n }\n }\n switch (code) {\n case C_NEWLINE:\n parseNewline()\n break\n case C_BACKSLASH:\n parseBackslash()\n break\n case C_BACKTICK:\n parseBackticks()\n break\n case C_ASTERISK:\n case C_UNDERSCORE:\n parseDelimiters(code)\n break\n case C_TILDE:\n if (context.gfm) parseDelimiters(code)\n else {\n pos++\n text('~')\n }\n break\n case C_OPEN_BRACKET:\n parseOpenBracket()\n break\n case C_BANG:\n parseBang()\n break\n case C_CLOSE_BRACKET:\n parseCloseBracket()\n break\n case C_LESSTHAN:\n parseLessThan()\n break\n case C_AMPERSAND:\n parseEntity()\n break\n default: {\n // A run of plain text, up to the next character that can start something.\n const start = pos\n pos++\n while (pos < end) {\n const next = subject.charCodeAt(pos)\n if (next < 128 && special[next] === 1) break\n pos++\n }\n text(subject.slice(start, pos))\n }\n }\n }\n processEmphasis(null)\n const nodes = finish(root, bareLinks)\n subject = ''\n return nodes\n}\n\n/**\n * Reads one link reference definition from the start of a paragraph's text into `refs`. Returns\n * how many characters it took, or 0 when the text does not start with a definition.\n */\nexport const parseReference = (source: string, refs: Map<string, Reference>): number => {\n subject = source\n pos = 0\n end = source.length\n const labelLength = parseLinkLabel()\n if (labelLength === 0) return 0\n const label = source.slice(0, labelLength)\n if (source.charCodeAt(pos) !== C_COLON) return 0\n pos++\n spnl()\n const destination = parseLinkDestination()\n if (destination === null) return 0\n const beforeTitle = pos\n spnl()\n let title: string | null = null\n if (pos !== beforeTitle) title = parseLinkTitle()\n if (title === null) pos = beforeTitle\n let atLineEnd = match(reSpaceAtEndOfLine) !== null\n if (!atLineEnd && title !== null) {\n // What follows the title is not the end of the line: the definition is the part before it.\n title = null\n pos = beforeTitle\n atLineEnd = match(reSpaceAtEndOfLine) !== null\n }\n if (!atLineEnd) return 0\n const normalized = normalizeLabel(label)\n if (normalized === '') return 0\n if (!refs.has(normalized)) refs.set(normalized, { url: destination, title: title ?? '' })\n return pos\n}\n\n/** The characters that start an inline construct, as a table by ASCII code. */\nexport const specialCharacters = (extra: string): Uint8Array => {\n const table = new Uint8Array(128)\n for (const char of `\\n\\\\\\`*_~[]!<&${extra}`) table[char.charCodeAt(0)] = 1\n return table\n}\n"],"mappings":"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"}
|
package/dist/react/code-block.js
CHANGED
|
@@ -4,6 +4,7 @@ import { code, codeBar, codeFrame, copyButton, downloadButton, readCodeBlock } f
|
|
|
4
4
|
import { useRenderContext } from "./context.js";
|
|
5
5
|
import { useLabels } from "./labels.js";
|
|
6
6
|
import { react } from "./render.js";
|
|
7
|
+
import { useScrolls } from "./scrolls.js";
|
|
7
8
|
import { memo, useEffect, useRef, useState } from "react";
|
|
8
9
|
import { jsx } from "react/jsx-runtime";
|
|
9
10
|
// src/react/code-block.tsx
|
|
@@ -66,15 +67,27 @@ const CodeBar = memo(function CodeBar({ language, title, code: text }) {
|
|
|
66
67
|
* language after it; a fence with no language shows none.
|
|
67
68
|
*/
|
|
68
69
|
function CodeFrame({ language, title, text, preProps, children }) {
|
|
70
|
+
const labels = useLabels();
|
|
71
|
+
const [attach, scrolls, again] = useScrolls();
|
|
69
72
|
const current = useRef(text);
|
|
70
73
|
useEffect(() => {
|
|
74
|
+
if (current.current === text) return;
|
|
71
75
|
current.current = text;
|
|
72
|
-
|
|
76
|
+
again();
|
|
77
|
+
}, [text, again]);
|
|
73
78
|
return codeFrame(react, language, /* @__PURE__ */ jsx(CodeBar, {
|
|
74
79
|
language,
|
|
75
80
|
title,
|
|
76
81
|
code: current
|
|
77
|
-
}),
|
|
82
|
+
}), {
|
|
83
|
+
...preProps,
|
|
84
|
+
ref: attach,
|
|
85
|
+
...scrolls ? {
|
|
86
|
+
role: "group",
|
|
87
|
+
"aria-label": labels.scrollableCode,
|
|
88
|
+
tabIndex: 0
|
|
89
|
+
} : null
|
|
90
|
+
}, [children]);
|
|
78
91
|
}
|
|
79
92
|
export { Code, CodeBlock, CodeFrame };
|
|
80
93
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-block.js","names":[],"sources":["../../src/react/code-block.tsx"],"sourcesContent":["'use client'\nimport {\n memo,\n useEffect,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from 'react'\nimport { codeFileName, copyText, downloadFile } from '../browser/file-actions'\nimport type { Props } from '../render/factory'\nimport {\n code,\n codeBar,\n codeFrame,\n copyButton,\n downloadButton,\n readCodeBlock,\n} from '../render/views'\nimport { useRenderContext } from './context'\nimport { useLabels } from './labels'\nimport { react } from './render'\n\ntype CodeProps = ComponentPropsWithoutRef<'code'> & { node?: unknown }\ntype PreProps = ComponentPropsWithoutRef<'pre'> & { node?: unknown }\n\n/** How long \"Copied\" stays before the button reads \"Copy\" again. */\nconst COPIED_MS = 1_600\n\n/**\n * Code, inline or in a fence. A fenced block is highlighted when the page gave `<Markdown>` a\n * highlighter and the fence names a language it knows; otherwise its text is printed as is.\n */\nexport function Code({ children, ...props }: CodeProps) {\n const { highlight } = useRenderContext()\n return code(react, props as Props, children === undefined ? [] : [children], highlight)\n}\n\n/** The code of a block as last drawn: read when a button is pressed, not when it is drawn. */\ntype CodeText = { readonly current: string }\n\nfunction CopyButton({ code: text }: { code: CodeText }) {\n const labels = useLabels()\n // How many times the code was copied since the button last read \"Copy\": each copy starts the\n // wait over, and the effect's cleanup ends it when the block leaves the page.\n const [copies, setCopies] = useState(0)\n useEffect(() => {\n if (copies === 0) return\n const timer = setTimeout(() => setCopies(0), COPIED_MS)\n return () => clearTimeout(timer)\n }, [copies])\n const copy = () => {\n // The Clipboard API, or the old copy command where the page has none (an insecure origin).\n // When both refuse there is nothing to say: the code is still selectable.\n void copyText(text.current).then((done) => {\n if (done) setCopies((count) => count + 1)\n })\n }\n return copyButton(react, labels, copies > 0, copy)\n}\n\n/** Saves the code as a file: named as its fence's title, or `code.<ext>` from its language. */\nfunction DownloadButton({ code: text, name }: { code: CodeText; name: string }) {\n return downloadButton(react, useLabels(), () => downloadFile(text.current, name))\n}\n\n/**\n * A fenced code block: its language, a download and a copy button above the code. Replace it with\n * your own through `components.pre`; the `Code` inside still highlights.\n */\nexport function CodeBlock({ node: _node, children, ...props }: PreProps) {\n const { language, title, text } = readCodeBlock(react, children === undefined ? [] : [children])\n return (\n <CodeFrame language={language} title={title} text={text} preProps={props}>\n {children}\n </CodeFrame>\n )\n}\n\n/**\n * The bar of a code block. It reads the code only when a button is pressed, so while a fence is\n * being written the bar is drawn once, not on every token.\n */\nconst CodeBar = memo(function CodeBar({\n language,\n title,\n code: text,\n}: {\n language: string | undefined\n title: string | undefined\n code: CodeText\n}) {\n return codeBar(react, language, title, [\n <DownloadButton key=\"download\" code={text} name={codeFileName(title, language)} />,\n <CopyButton key=\"copy\" code={text} />,\n ])\n})\n\n/**\n * The frame of a code block: the bar with its language and its download and copy buttons, then the\n * `<pre>`. A fence that names its file (` ```ts title=\"src/app.ts\" `) shows the file first, the\n * language after it; a fence with no language shows none.\n */\nexport function CodeFrame({\n language,\n title,\n text,\n preProps,\n children,\n}: {\n language: string | undefined\n title?: string | undefined\n text: string\n preProps?: ComponentPropsWithoutRef<'pre'>\n children: ReactNode\n}) {\n const current = useRef(text)\n useEffect(() => {\n current.current = text\n }, [text])\n return codeFrame(\n react,\n language,\n <CodeBar language={language} title={title} code={current} />,\n preProps as Props | undefined,\n [children],\n )\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"code-block.js","names":[],"sources":["../../src/react/code-block.tsx"],"sourcesContent":["'use client'\nimport {\n memo,\n useEffect,\n useRef,\n useState,\n type ComponentPropsWithoutRef,\n type ReactNode,\n} from 'react'\nimport { codeFileName, copyText, downloadFile } from '../browser/file-actions'\nimport type { Props } from '../render/factory'\nimport {\n code,\n codeBar,\n codeFrame,\n copyButton,\n downloadButton,\n readCodeBlock,\n} from '../render/views'\nimport { useRenderContext } from './context'\nimport { useLabels } from './labels'\nimport { react } from './render'\nimport { useScrolls } from './scrolls'\n\ntype CodeProps = ComponentPropsWithoutRef<'code'> & { node?: unknown }\ntype PreProps = ComponentPropsWithoutRef<'pre'> & { node?: unknown }\n\n/** How long \"Copied\" stays before the button reads \"Copy\" again. */\nconst COPIED_MS = 1_600\n\n/**\n * Code, inline or in a fence. A fenced block is highlighted when the page gave `<Markdown>` a\n * highlighter and the fence names a language it knows; otherwise its text is printed as is.\n */\nexport function Code({ children, ...props }: CodeProps) {\n const { highlight } = useRenderContext()\n return code(react, props as Props, children === undefined ? [] : [children], highlight)\n}\n\n/** The code of a block as last drawn: read when a button is pressed, not when it is drawn. */\ntype CodeText = { readonly current: string }\n\nfunction CopyButton({ code: text }: { code: CodeText }) {\n const labels = useLabels()\n // How many times the code was copied since the button last read \"Copy\": each copy starts the\n // wait over, and the effect's cleanup ends it when the block leaves the page.\n const [copies, setCopies] = useState(0)\n useEffect(() => {\n if (copies === 0) return\n const timer = setTimeout(() => setCopies(0), COPIED_MS)\n return () => clearTimeout(timer)\n }, [copies])\n const copy = () => {\n // The Clipboard API, or the old copy command where the page has none (an insecure origin).\n // When both refuse there is nothing to say: the code is still selectable.\n void copyText(text.current).then((done) => {\n if (done) setCopies((count) => count + 1)\n })\n }\n return copyButton(react, labels, copies > 0, copy)\n}\n\n/** Saves the code as a file: named as its fence's title, or `code.<ext>` from its language. */\nfunction DownloadButton({ code: text, name }: { code: CodeText; name: string }) {\n return downloadButton(react, useLabels(), () => downloadFile(text.current, name))\n}\n\n/**\n * A fenced code block: its language, a download and a copy button above the code. Replace it with\n * your own through `components.pre`; the `Code` inside still highlights.\n */\nexport function CodeBlock({ node: _node, children, ...props }: PreProps) {\n const { language, title, text } = readCodeBlock(react, children === undefined ? [] : [children])\n return (\n <CodeFrame language={language} title={title} text={text} preProps={props}>\n {children}\n </CodeFrame>\n )\n}\n\n/**\n * The bar of a code block. It reads the code only when a button is pressed, so while a fence is\n * being written the bar is drawn once, not on every token.\n */\nconst CodeBar = memo(function CodeBar({\n language,\n title,\n code: text,\n}: {\n language: string | undefined\n title: string | undefined\n code: CodeText\n}) {\n return codeBar(react, language, title, [\n <DownloadButton key=\"download\" code={text} name={codeFileName(title, language)} />,\n <CopyButton key=\"copy\" code={text} />,\n ])\n})\n\n/**\n * The frame of a code block: the bar with its language and its download and copy buttons, then the\n * `<pre>`. A fence that names its file (` ```ts title=\"src/app.ts\" `) shows the file first, the\n * language after it; a fence with no language shows none.\n */\nexport function CodeFrame({\n language,\n title,\n text,\n preProps,\n children,\n}: {\n language: string | undefined\n title?: string | undefined\n text: string\n preProps?: ComponentPropsWithoutRef<'pre'>\n children: ReactNode\n}) {\n const labels = useLabels()\n // A line too long for the reply scrolls inside the `<pre>`: the keyboard must then reach it.\n const [attach, scrolls, again] = useScrolls<HTMLPreElement>()\n const current = useRef(text)\n useEffect(() => {\n // Not on the first drawing: the box was measured as it was attached.\n if (current.current === text) return\n current.current = text\n again()\n }, [text, again])\n return codeFrame(\n react,\n language,\n <CodeBar language={language} title={title} code={current} />,\n {\n ...(preProps as Props | undefined),\n ref: attach,\n ...(scrolls ? { role: 'group', 'aria-label': labels.scrollableCode, tabIndex: 0 } : null),\n },\n [children],\n )\n}\n"],"mappings":";;;;;;;;;;;AA4BA,MAAM,YAAY;;;;;AAMlB,SAAgB,KAAK,EAAE,UAAU,GAAG,SAAoB;CACtD,MAAM,EAAE,cAAc,iBAAiB;CACvC,OAAO,KAAK,OAAO,OAAgB,aAAa,KAAA,IAAY,CAAC,IAAI,CAAC,QAAQ,GAAG,SAAS;AACxF;AAKA,SAAS,WAAW,EAAE,MAAM,QAA4B;CACtD,MAAM,SAAS,UAAU;CAGzB,MAAM,CAAC,QAAQ,aAAa,SAAS,CAAC;CACtC,gBAAgB;EACd,IAAI,WAAW,GAAG;EAClB,MAAM,QAAQ,iBAAiB,UAAU,CAAC,GAAG,SAAS;EACtD,aAAa,aAAa,KAAK;CACjC,GAAG,CAAC,MAAM,CAAC;CACX,MAAM,aAAa;EAGjB,SAAc,KAAK,OAAO,CAAC,CAAC,MAAM,SAAS;GACzC,IAAI,MAAM,WAAW,UAAU,QAAQ,CAAC;EAC1C,CAAC;CACH;CACA,OAAO,WAAW,OAAO,QAAQ,SAAS,GAAG,IAAI;AACnD;;AAGA,SAAS,eAAe,EAAE,MAAM,MAAM,QAA0C;CAC9E,OAAO,eAAe,OAAO,UAAU,SAAS,aAAa,KAAK,SAAS,IAAI,CAAC;AAClF;;;;;AAMA,SAAgB,UAAU,EAAE,MAAM,OAAO,UAAU,GAAG,SAAmB;CACvE,MAAM,EAAE,UAAU,OAAO,SAAS,cAAc,OAAO,aAAa,KAAA,IAAY,CAAC,IAAI,CAAC,QAAQ,CAAC;CAC/F,OACE,oBAAC,WAAD;EAAqB;EAAiB;EAAa;EAAM,UAAU;EAChE;CACQ,CAAA;AAEf;;;;;AAMA,MAAM,UAAU,KAAK,SAAS,QAAQ,EACpC,UACA,OACA,MAAM,QAKL;CACD,OAAO,QAAQ,OAAO,UAAU,OAAO,CACrC,oBAAC,gBAAD;EAA+B,MAAM;EAAM,MAAM,aAAa,OAAO,QAAQ;CAAI,GAA7D,UAA6D,GACjF,oBAAC,YAAD,EAAuB,MAAM,KAAO,GAApB,MAAoB,CACtC,CAAC;AACH,CAAC;;;;;;AAOD,SAAgB,UAAU,EACxB,UACA,OACA,MACA,UACA,YAOC;CACD,MAAM,SAAS,UAAU;CAEzB,MAAM,CAAC,QAAQ,SAAS,SAAS,WAA2B;CAC5D,MAAM,UAAU,OAAO,IAAI;CAC3B,gBAAgB;EAEd,IAAI,QAAQ,YAAY,MAAM;EAC9B,QAAQ,UAAU;EAClB,MAAM;CACR,GAAG,CAAC,MAAM,KAAK,CAAC;CAChB,OAAO,UACL,OACA,UACA,oBAAC,SAAD;EAAmB;EAAiB;EAAO,MAAM;CAAU,CAAA,GAC3D;EACE,GAAI;EACJ,KAAK;EACL,GAAI,UAAU;GAAE,MAAM;GAAS,cAAc,OAAO;GAAgB,UAAU;EAAE,IAAI;CACtF,GACA,CAAC,QAAQ,CACX;AACF"}
|
package/dist/react/draw.js
CHANGED
package/dist/react/draw.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"draw.js","names":[],"sources":["../../src/react/draw.ts"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { parse } from '../engine'\nimport type { Extension } from '../engine/types'\nimport type { UrlTransform } from '../engine/url'\nimport { render, type Components, type RenderOptions } from './render'\n\n/** What a block needs besides its text; one object per document, so blocks can compare it. */\nexport interface BlockOptions {\n components: Components\n /** The syntax every block is read with: the dialect, the stages that are there, the page's. */\n extensions: Extension[]\n urlTransform: UrlTransform\n special: RenderOptions['special']\n /** The page draws code itself (`components.pre` or `.code`): a growing fence goes to it too. */\n ownCode: boolean\n /** The page draws tables itself, or its extensions read them: a table is then parsed whole. */\n ownTable: boolean\n}\n\n/** One text read and drawn: a whole document, or one of its blocks. */\nexport function draw(\n source: string,\n offset: number,\n headingIds: ReadonlyMap<number, string> | null,\n options: BlockOptions,\n): ReactNode {\n return render(parse(source, { extensions: options.extensions }), {\n components: options.components,\n urlTransform: options.urlTransform,\n special: options.special,\n offset,\n headingIds,\n })\n}\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"draw.js","names":[],"sources":["../../src/react/draw.ts"],"sourcesContent":["import type { ReactNode } from 'react'\nimport { parse } from '../engine'\nimport type { Extension } from '../engine/types'\nimport type { UrlTransform } from '../engine/url'\nimport { render, type Components, type RenderOptions } from './render'\n\n/** What a block needs besides its text; one object per document, so blocks can compare it. */\nexport interface BlockOptions {\n components: Components\n /** The syntax every block is read with: the dialect, the stages that are there, the page's. */\n extensions: Extension[]\n urlTransform: UrlTransform\n special: RenderOptions['special']\n /** The page draws code itself (`components.pre` or `.code`): a growing fence goes to it too. */\n ownCode: boolean\n /** The page draws tables itself, or its extensions read them: a table is then parsed whole. */\n ownTable: boolean\n /** The names of a task list's boxes. */\n labels?: RenderOptions['labels']\n}\n\n/** One text read and drawn: a whole document, or one of its blocks. */\nexport function draw(\n source: string,\n offset: number,\n headingIds: ReadonlyMap<number, string> | null,\n options: BlockOptions,\n): ReactNode {\n return render(parse(source, { extensions: options.extensions }), {\n components: options.components,\n urlTransform: options.urlTransform,\n special: options.special,\n labels: options.labels,\n offset,\n headingIds,\n })\n}\n"],"mappings":";;;;AAsBA,SAAgB,KACd,QACA,QACA,YACA,SACW;CACX,OAAO,OAAO,MAAM,QAAQ,EAAE,YAAY,QAAQ,WAAW,CAAC,GAAG;EAC/D,YAAY,QAAQ;EACpB,cAAc,QAAQ;EACtB,SAAS,QAAQ;EACjB,QAAQ,QAAQ;EAChB;EACA;CACF,CAAC;AACH"}
|
package/dist/react/markdown.js
CHANGED
|
@@ -145,8 +145,10 @@ function Markdown({ children, streaming: arriving = false, smooth = false, caret
|
|
|
145
145
|
const math = useStage(mathOption, mathOption !== null && usesMath(blocks));
|
|
146
146
|
const htmlStage = useStage(html === true ? loadHtml : html === false ? null : html, html !== false && usesHtml(blocks));
|
|
147
147
|
const entities = useStage(loadEntities, !hasAllEntities() && usesRareEntity(blocks));
|
|
148
|
+
const labelValue = useMemo(() => mergeLabels(labels), [labels]);
|
|
148
149
|
const options = useMemo(() => ({
|
|
149
150
|
...buildComponents(components),
|
|
151
|
+
labels: labelValue,
|
|
150
152
|
extensions: [
|
|
151
153
|
flowdownDialect,
|
|
152
154
|
...math ? [math] : [],
|
|
@@ -162,7 +164,8 @@ function Markdown({ children, streaming: arriving = false, smooth = false, caret
|
|
|
162
164
|
urlTransform,
|
|
163
165
|
components,
|
|
164
166
|
extensions,
|
|
165
|
-
entities
|
|
167
|
+
entities,
|
|
168
|
+
labelValue
|
|
166
169
|
]);
|
|
167
170
|
const context = useMemo(() => {
|
|
168
171
|
if (mermaid) setMermaidLoader(mermaid);
|
|
@@ -171,7 +174,6 @@ function Markdown({ children, streaming: arriving = false, smooth = false, caret
|
|
|
171
174
|
mermaid: mermaid ? "page" : null
|
|
172
175
|
};
|
|
173
176
|
}, [highlight, mermaid]);
|
|
174
|
-
const labelValue = useMemo(() => mergeLabels(labels), [labels]);
|
|
175
177
|
const groups = [];
|
|
176
178
|
for (let from = 0; from < blocks.length; from += GROUP) {
|
|
177
179
|
const to = Math.min(from + GROUP, blocks.length);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"markdown.js","names":[],"sources":["../../src/react/markdown.tsx"],"sourcesContent":["'use client'\nimport { DEFAULT_CATCH_UP_MS, useSmoothText } from './use-smooth-text'\nimport { useContext, Fragment, memo, useMemo, useState, type ReactNode } from 'react'\nimport { normalizeLineEndings, type MarkdownBlock } from '../core/blocks'\nimport { createDocument, usesHtml, usesMath, usesRareEntity } from '../document'\nimport { flowdownDialect } from '../core/remark-answer-blocks'\nimport { DIAGRAM_FENCE_LANGUAGE } from '../dialect'\nimport { addEntities, hasAllEntities } from '../engine/entities'\nimport type { Extension } from '../engine/types'\nimport { Diagram } from '../mermaid/diagram'\nimport { setMermaidLoader, type MermaidLoader } from '../mermaid/loader'\nimport { Callout } from './callout'\nimport { Code, CodeBlock } from './code-block'\nimport { createFenceMemory, GrowingCode, growingFence } from './growing-code'\nimport {\n GrowingFence,\n RenderContextValue,\n type Highlighter,\n type MarkdownComponents,\n type RenderContext,\n} from './context'\nimport { FileTree } from './file-tree'\nimport { LabelsContext, mergeLabels, type LabelOverrides } from './labels'\nimport { defaultUrlTransform, type UrlTransform } from '../engine/url'\nimport { draw, type BlockOptions } from './draw'\nimport type { Components, RenderOptions } from './render'\nimport { useStage, type StageOption } from './use-stage'\nimport { Table } from './table'\nimport { TableRows, tableRows } from './table-rows'\nimport { Tab, Tabs } from './tabs'\n\nexport interface MarkdownProps {\n /** The Markdown. */\n children: string\n /**\n * The text is a reply still arriving. Its unfinished end is completed so it does not flash raw\n * syntax (`**bo` reads as bold, a fence still being written as code), and a caret follows it.\n */\n streaming?: boolean\n /**\n * Show a streaming reply at an even pace instead of in the bursts it arrives in: whole words,\n * a few frames behind the text, finishing at the same pace when the reply ends. `true`, or the\n * time in milliseconds a burst takes to show (240 by default). Off by default.\n */\n smooth?: boolean | number\n /** The caret after streaming text; on by default while `streaming`. */\n caret?: boolean\n /**\n * Your own elements for any tag (`a`, `img`, `pre`, `table`, …) or the dialect's, and your own\n * drawing of a ```mermaid fence (`diagram`) or a ```tree fence (`tree`).\n */\n components?: MarkdownComponents\n /**\n * Your own syntax, after the dialect's: inline rules, fences by language, and passes over the\n * tree, each run on every block. A node's position counts from the start of its block. Keep the\n * list's reference stable (a module constant): a new list reads every block again.\n */\n extensions?: Extension[]\n /**\n * Give headings ids (`setup`, `setup-1`) to link to, the same ids `extractHeadings` returns.\n * Each block's headings are read once, so it costs a streaming reply only its last block.\n */\n headingIds?: boolean\n /**\n * Raw HTML in the text, parsed and sanitized. `true` (the default) fetches the parser the first\n * time a document holds HTML, and leaves the tags out until it is there; pass `@localix-ai/flowdown/html`\n * itself to have it in the first render (a server); `false` drops raw HTML.\n */\n html?: boolean | StageOption<Extension>\n /** Typeset `$…$`: `@localix-ai/flowdown/math`, or a loader for it. Off by default. */\n math?: StageOption<Extension>\n /** Draw ```mermaid fences: `() => import('mermaid')`. Off by default: they show as code. */\n mermaid?: MermaidLoader | null\n /** Highlight fenced code: `@localix-ai/flowdown/highlight`, or your own. Off by default. */\n highlight?: Highlighter | null\n /** The words the renderer adds (callout names, button labels), for a page in another language. */\n labels?: LabelOverrides\n /**\n * Rewrite or refuse a link or image URL. Omitted (or null), relative URLs and http, https,\n * mailto, irc, ircs and xmpp are kept and everything else is emptied (`javascript:`, `tel:` and\n * `data:` among them).\n */\n urlTransform?: UrlTransform | null\n className?: string\n /**\n * Leaves the `flowdown` class off the root element, so the package's stylesheet does not\n * apply: for a small surface that draws every element with `components` at its own scale.\n */\n unstyled?: boolean\n}\n\ninterface BlockProps {\n source: string\n /**\n * The line that closes the fence the reply ends inside, on the last block only: kept beside\n * the text, not joined to it, so a long fence is not copied on every token.\n */\n closer: string\n offset: number\n growing: boolean\n /** The anchors of the headings inside this block, by their offset in the document. */\n headingIds: ReadonlyMap<number, string> | null\n /** The same anchors as text: the memo compares this, since the map is new on every render. */\n headingKey: string\n options: BlockOptions\n}\n\nexport { draw, type BlockOptions } from './draw'\n\n/** The package's own HTML stage, fetched as a chunk of its own the first time it is needed. */\nconst loadHtml = () => import('../html/index')\n\n/** Every HTML5 named reference: ten kilobytes fetched the first time a document uses a rare one. */\nconst loadEntities = () =>\n import('../engine/entity-table').then((module) => {\n addEntities(module.ENTITY_TABLE)\n return module\n })\n\n/** A page's own diagram, told whether its fence is the one still being written. */\nfunction OwnDiagram({\n as: Page,\n source,\n}: {\n as: NonNullable<MarkdownComponents['diagram']>\n source: string\n}) {\n return <Page source={source} growing={useContext(GrowingFence)} />\n}\n\n/**\n * The elements a document is drawn with: the dialect's (callouts, tabs, code blocks), then the\n * page's. The two fences that are not code (```mermaid, ```tree) are drawn by `special`, outside\n * both `pre` and `code`, so a page's own code block never receives a diagram.\n */\nexport function buildComponents(overrides: MarkdownComponents | undefined): {\n components: Components\n special: RenderOptions['special']\n} {\n const { diagram: PageDiagram, tree: PageTree, ...elements } = overrides ?? {}\n return {\n components: {\n blockquote: Callout,\n table: Table,\n 'answer-tabs': Tabs,\n 'answer-tab': Tab,\n pre: CodeBlock,\n code: Code,\n ...elements,\n } as Components,\n special: (language, source) =>\n language === DIAGRAM_FENCE_LANGUAGE ? (\n PageDiagram ? (\n <OwnDiagram as={PageDiagram} source={source} />\n ) : (\n <Diagram source={source} />\n )\n ) : PageTree ? (\n <PageTree source={source} />\n ) : (\n <FileTree source={source} />\n ),\n }\n}\n\nconst Block = memo(\n function Block({ source: open, closer, offset, growing, headingIds, options }: BlockProps) {\n const [memory] = useState(createFenceMemory)\n const fence = growing && !options.ownCode ? growingFence(open, closer, memory) : null\n if (\n fence &&\n fence.language !== DIAGRAM_FENCE_LANGUAGE &&\n fence.language !== 'tree' &&\n // A `math` fence is a formula where the page typesets math, and code where it does not.\n !(fence.language === 'math' && options.extensions?.some((stage) => stage.name === 'math'))\n )\n return <GrowingCode {...fence} />\n const source = closer ? open + closer : open\n // A table that is the whole block is drawn row by row, unless the page reads tables itself\n // (its own elements or extensions see the table whole).\n const table = options.ownTable ? null : tableRows(source)\n if (table) return <TableRows {...table} options={options} />\n return (\n <GrowingFence.Provider value={growing}>\n {draw(source, offset, headingIds, options)}\n </GrowingFence.Provider>\n )\n },\n (before, after) =>\n before.source === after.source &&\n before.closer === after.closer &&\n before.offset === after.offset &&\n before.growing === after.growing &&\n before.headingKey === after.headingKey &&\n before.options === after.options,\n)\n\nconst NO_EXTENSIONS: Extension[] = []\n\n/**\n * How many blocks a group holds. Small enough that the group a reply is growing in is cheap to\n * go through on every token, large enough that a long reply is few groups.\n */\nconst GROUP = 32\n\nconst headingKeyOf = (own: ReadonlyMap<number, string> | null | undefined): string =>\n own ? [...own].map(([at, id]) => `${at}:${id}`).join(';') : ''\n\ninterface BlockGroupProps {\n /** The document's blocks and anchors; the group draws the ones from `from` up to `to`. */\n blocks: readonly MarkdownBlock[]\n ids: ReadonlyArray<ReadonlyMap<number, string>> | null\n from: number\n to: number\n /** The closing line and the growing fence of the document's last block, when it is in here. */\n closer: string\n growing: boolean\n options: BlockOptions\n}\n\n/**\n * The blocks at one run of places in the document. A block never moves to another group, so it\n * keeps what it holds (an open tab, a drawn diagram) for as long as it is there. While a reply\n * streams only its last group changes: React passes over each group above as one element, where\n * a flat list had it compare every block of the reply on every token.\n */\nconst BlockGroup = memo(\n function BlockGroup({ blocks, ids, from, to, closer, growing, options }: BlockGroupProps) {\n const drawn: ReactNode[] = []\n for (let index = from; index < to; index++) {\n const block = blocks[index]!\n // A block gets only its own anchors, keyed by their text: a map is new on every render,\n // and comparing it would redraw every block.\n const own = ids?.[index] ?? null\n const last = index === to - 1\n drawn.push(\n <Fragment key={index}>\n {/* One parse puts a line break between top-level blocks; so does this, so the text of\n the page (and a copy of it) is the same either way. */}\n {index > 0 ? '\\n' : null}\n <Block\n source={block.source}\n closer={last ? closer : ''}\n offset={block.offset}\n growing={last && growing}\n headingIds={own && own.size > 0 ? own : null}\n headingKey={headingKeyOf(own)}\n options={options}\n />\n </Fragment>,\n )\n }\n return drawn\n },\n (before, after) => {\n if (\n before.from !== after.from ||\n before.to !== after.to ||\n before.closer !== after.closer ||\n before.growing !== after.growing ||\n before.options !== after.options ||\n (before.ids === null) !== (after.ids === null)\n )\n return false\n // From the end: in the group a reply is growing in, the block that changed is the last one.\n for (let index = after.to - 1; index >= after.from; index--) {\n const was = before.blocks[index]!\n const is = after.blocks[index]!\n // The splitter hands back a block that did not change as the same object.\n if (was !== is && (was.source !== is.source || was.offset !== is.offset)) return false\n if (after.ids) {\n const had = before.ids![index]\n const has = after.ids[index]\n // Most blocks have no heading: two empty maps are not spelled out to be compared.\n if (had !== has && ((had?.size ?? 0) > 0 || (has?.size ?? 0) > 0)) {\n if (headingKeyOf(had) !== headingKeyOf(has)) return false\n }\n }\n }\n return true\n },\n)\n\n/**\n * Markdown with the flowdown dialect: GitHub Markdown, callouts, steps, tabs, stats, pros and cons,\n * figures, file trees and, when the page supplies them, diagrams, formulas and highlighted code.\n *\n * The document is drawn as its top-level blocks, each remembered by its text: while a reply\n * streams, the blocks above its end do not change, and only the last one is parsed and drawn\n * again on each token.\n */\nexport function Markdown({\n children,\n streaming: arriving = false,\n smooth = false,\n caret: caretOption,\n components,\n extensions = NO_EXTENSIONS,\n headingIds = false,\n html = true,\n math: mathOption = null,\n mermaid = null,\n highlight = null,\n labels,\n urlTransform,\n className,\n unstyled = false,\n}: MarkdownProps): ReactNode {\n // `\\r\\n` from a Windows file or a server that sends it reads as `\\n` everywhere below.\n const whole = normalizeLineEndings(children)\n // Paced, the reply is still \"streaming\" until all of it is shown, a moment after it ended.\n const [text, behind] = useSmoothText(\n whole,\n arriving && smooth !== false,\n typeof smooth === 'number' ? smooth : DEFAULT_CATCH_UP_MS,\n )\n const streaming = arriving || behind\n const caret = caretOption ?? streaming\n // One document per `<Markdown>`: it completes the unfinished end of a streaming reply, cuts the\n // text into blocks and remembers them, so on the next token only the end of the reply is read.\n // Blocks that did not change come back as the same objects.\n const [doc] = useState(createDocument)\n const {\n blocks,\n closer,\n growing: endsInFence,\n headingIds: ids,\n } = doc.update(text, { streaming, math: Boolean(mathOption), headingIds })\n\n const math = useStage(mathOption, mathOption !== null && usesMath(blocks))\n // Whether the document has HTML is decided for the whole of it, not per block: a tag still\n // being typed (`<div`) is its own block, and only the whole document says it is HTML.\n const htmlStage = useStage(\n html === true ? loadHtml : html === false ? null : html,\n html !== false && usesHtml(blocks),\n )\n // A name outside the common ones shows as written until the whole table is there.\n const entities = useStage(loadEntities, !hasAllEntities() && usesRareEntity(blocks))\n const options = useMemo<BlockOptions>(\n () => ({\n ...buildComponents(components),\n // Raw HTML is made into elements last, so every pass before it sees the text as written.\n extensions: [\n flowdownDialect,\n ...(math ? [math] : []),\n ...extensions,\n ...(htmlStage ? [htmlStage] : []),\n ],\n urlTransform: urlTransform ?? defaultUrlTransform,\n ownCode: Boolean(components?.pre || components?.code),\n ownTable: Boolean(\n components?.table || components?.thead || components?.tbody || extensions.length,\n ),\n }),\n // `entities` is here so that blocks are read again once every name can be decoded.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n [htmlStage, math, urlTransform, components, extensions, entities],\n )\n const context = useMemo<RenderContext>(() => {\n if (mermaid) setMermaidLoader(mermaid)\n return { highlight, mermaid: mermaid ? 'page' : null }\n }, [highlight, mermaid])\n const labelValue = useMemo(() => mergeLabels(labels), [labels])\n const groups: ReactNode[] = []\n for (let from = 0; from < blocks.length; from += GROUP) {\n const to = Math.min(from + GROUP, blocks.length)\n const last = to === blocks.length\n groups.push(\n <BlockGroup\n key={from}\n blocks={blocks}\n ids={ids}\n from={from}\n to={to}\n closer={last ? closer : ''}\n growing={last && endsInFence}\n options={options}\n />,\n )\n }\n\n return (\n <LabelsContext.Provider value={labelValue}>\n <RenderContextValue.Provider value={context}>\n <div\n className={unstyled ? className : className ? `flowdown ${className}` : 'flowdown'}\n data-streaming={streaming || undefined}\n data-caret={(streaming && caret) || undefined}\n >\n {groups}\n </div>\n </RenderContextValue.Provider>\n </LabelsContext.Provider>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"markdown.js","names":[],"sources":["../../src/react/markdown.tsx"],"sourcesContent":["'use client'\nimport { DEFAULT_CATCH_UP_MS, useSmoothText } from './use-smooth-text'\nimport { useContext, Fragment, memo, useMemo, useState, type ReactNode } from 'react'\nimport { normalizeLineEndings, type MarkdownBlock } from '../core/blocks'\nimport { createDocument, usesHtml, usesMath, usesRareEntity } from '../document'\nimport { flowdownDialect } from '../core/remark-answer-blocks'\nimport { DIAGRAM_FENCE_LANGUAGE } from '../dialect'\nimport { addEntities, hasAllEntities } from '../engine/entities'\nimport type { Extension } from '../engine/types'\nimport { Diagram } from '../mermaid/diagram'\nimport { setMermaidLoader, type MermaidLoader } from '../mermaid/loader'\nimport { Callout } from './callout'\nimport { Code, CodeBlock } from './code-block'\nimport { createFenceMemory, GrowingCode, growingFence } from './growing-code'\nimport {\n GrowingFence,\n RenderContextValue,\n type Highlighter,\n type MarkdownComponents,\n type RenderContext,\n} from './context'\nimport { FileTree } from './file-tree'\nimport { LabelsContext, mergeLabels, type LabelOverrides } from './labels'\nimport { defaultUrlTransform, type UrlTransform } from '../engine/url'\nimport { draw, type BlockOptions } from './draw'\nimport type { Components, RenderOptions } from './render'\nimport { useStage, type StageOption } from './use-stage'\nimport { Table } from './table'\nimport { TableRows, tableRows } from './table-rows'\nimport { Tab, Tabs } from './tabs'\n\nexport interface MarkdownProps {\n /** The Markdown. */\n children: string\n /**\n * The text is a reply still arriving. Its unfinished end is completed so it does not flash raw\n * syntax (`**bo` reads as bold, a fence still being written as code), and a caret follows it.\n */\n streaming?: boolean\n /**\n * Show a streaming reply at an even pace instead of in the bursts it arrives in: whole words,\n * a few frames behind the text, finishing at the same pace when the reply ends. `true`, or the\n * time in milliseconds a burst takes to show (240 by default). Off by default.\n */\n smooth?: boolean | number\n /** The caret after streaming text; on by default while `streaming`. */\n caret?: boolean\n /**\n * Your own elements for any tag (`a`, `img`, `pre`, `table`, …) or the dialect's, and your own\n * drawing of a ```mermaid fence (`diagram`) or a ```tree fence (`tree`).\n */\n components?: MarkdownComponents\n /**\n * Your own syntax, after the dialect's: inline rules, fences by language, and passes over the\n * tree, each run on every block. A node's position counts from the start of its block. Keep the\n * list's reference stable (a module constant): a new list reads every block again.\n */\n extensions?: Extension[]\n /**\n * Give headings ids (`setup`, `setup-1`) to link to, the same ids `extractHeadings` returns.\n * Each block's headings are read once, so it costs a streaming reply only its last block.\n */\n headingIds?: boolean\n /**\n * Raw HTML in the text, parsed and sanitized. `true` (the default) fetches the parser the first\n * time a document holds HTML, and leaves the tags out until it is there; pass `@localix-ai/flowdown/html`\n * itself to have it in the first render (a server); `false` drops raw HTML.\n */\n html?: boolean | StageOption<Extension>\n /** Typeset `$…$`: `@localix-ai/flowdown/math`, or a loader for it. Off by default. */\n math?: StageOption<Extension>\n /** Draw ```mermaid fences: `() => import('mermaid')`. Off by default: they show as code. */\n mermaid?: MermaidLoader | null\n /** Highlight fenced code: `@localix-ai/flowdown/highlight`, or your own. Off by default. */\n highlight?: Highlighter | null\n /** The words the renderer adds (callout names, button labels), for a page in another language. */\n labels?: LabelOverrides\n /**\n * Rewrite or refuse a link or image URL. Omitted (or null), relative URLs and http, https,\n * mailto, irc, ircs and xmpp are kept and everything else is emptied (`javascript:`, `tel:` and\n * `data:` among them).\n */\n urlTransform?: UrlTransform | null\n className?: string\n /**\n * Leaves the `flowdown` class off the root element, so the package's stylesheet does not\n * apply: for a small surface that draws every element with `components` at its own scale.\n */\n unstyled?: boolean\n}\n\ninterface BlockProps {\n source: string\n /**\n * The line that closes the fence the reply ends inside, on the last block only: kept beside\n * the text, not joined to it, so a long fence is not copied on every token.\n */\n closer: string\n offset: number\n growing: boolean\n /** The anchors of the headings inside this block, by their offset in the document. */\n headingIds: ReadonlyMap<number, string> | null\n /** The same anchors as text: the memo compares this, since the map is new on every render. */\n headingKey: string\n options: BlockOptions\n}\n\nexport { draw, type BlockOptions } from './draw'\n\n/** The package's own HTML stage, fetched as a chunk of its own the first time it is needed. */\nconst loadHtml = () => import('../html/index')\n\n/** Every HTML5 named reference: ten kilobytes fetched the first time a document uses a rare one. */\nconst loadEntities = () =>\n import('../engine/entity-table').then((module) => {\n addEntities(module.ENTITY_TABLE)\n return module\n })\n\n/** A page's own diagram, told whether its fence is the one still being written. */\nfunction OwnDiagram({\n as: Page,\n source,\n}: {\n as: NonNullable<MarkdownComponents['diagram']>\n source: string\n}) {\n return <Page source={source} growing={useContext(GrowingFence)} />\n}\n\n/**\n * The elements a document is drawn with: the dialect's (callouts, tabs, code blocks), then the\n * page's. The two fences that are not code (```mermaid, ```tree) are drawn by `special`, outside\n * both `pre` and `code`, so a page's own code block never receives a diagram.\n */\nexport function buildComponents(overrides: MarkdownComponents | undefined): {\n components: Components\n special: RenderOptions['special']\n} {\n const { diagram: PageDiagram, tree: PageTree, ...elements } = overrides ?? {}\n return {\n components: {\n blockquote: Callout,\n table: Table,\n 'answer-tabs': Tabs,\n 'answer-tab': Tab,\n pre: CodeBlock,\n code: Code,\n ...elements,\n } as Components,\n special: (language, source) =>\n language === DIAGRAM_FENCE_LANGUAGE ? (\n PageDiagram ? (\n <OwnDiagram as={PageDiagram} source={source} />\n ) : (\n <Diagram source={source} />\n )\n ) : PageTree ? (\n <PageTree source={source} />\n ) : (\n <FileTree source={source} />\n ),\n }\n}\n\nconst Block = memo(\n function Block({ source: open, closer, offset, growing, headingIds, options }: BlockProps) {\n const [memory] = useState(createFenceMemory)\n const fence = growing && !options.ownCode ? growingFence(open, closer, memory) : null\n if (\n fence &&\n fence.language !== DIAGRAM_FENCE_LANGUAGE &&\n fence.language !== 'tree' &&\n // A `math` fence is a formula where the page typesets math, and code where it does not.\n !(fence.language === 'math' && options.extensions?.some((stage) => stage.name === 'math'))\n )\n return <GrowingCode {...fence} />\n const source = closer ? open + closer : open\n // A table that is the whole block is drawn row by row, unless the page reads tables itself\n // (its own elements or extensions see the table whole).\n const table = options.ownTable ? null : tableRows(source)\n if (table) return <TableRows {...table} options={options} />\n return (\n <GrowingFence.Provider value={growing}>\n {draw(source, offset, headingIds, options)}\n </GrowingFence.Provider>\n )\n },\n (before, after) =>\n before.source === after.source &&\n before.closer === after.closer &&\n before.offset === after.offset &&\n before.growing === after.growing &&\n before.headingKey === after.headingKey &&\n before.options === after.options,\n)\n\nconst NO_EXTENSIONS: Extension[] = []\n\n/**\n * How many blocks a group holds. Small enough that the group a reply is growing in is cheap to\n * go through on every token, large enough that a long reply is few groups.\n */\nconst GROUP = 32\n\nconst headingKeyOf = (own: ReadonlyMap<number, string> | null | undefined): string =>\n own ? [...own].map(([at, id]) => `${at}:${id}`).join(';') : ''\n\ninterface BlockGroupProps {\n /** The document's blocks and anchors; the group draws the ones from `from` up to `to`. */\n blocks: readonly MarkdownBlock[]\n ids: ReadonlyArray<ReadonlyMap<number, string>> | null\n from: number\n to: number\n /** The closing line and the growing fence of the document's last block, when it is in here. */\n closer: string\n growing: boolean\n options: BlockOptions\n}\n\n/**\n * The blocks at one run of places in the document. A block never moves to another group, so it\n * keeps what it holds (an open tab, a drawn diagram) for as long as it is there. While a reply\n * streams only its last group changes: React passes over each group above as one element, where\n * a flat list had it compare every block of the reply on every token.\n */\nconst BlockGroup = memo(\n function BlockGroup({ blocks, ids, from, to, closer, growing, options }: BlockGroupProps) {\n const drawn: ReactNode[] = []\n for (let index = from; index < to; index++) {\n const block = blocks[index]!\n // A block gets only its own anchors, keyed by their text: a map is new on every render,\n // and comparing it would redraw every block.\n const own = ids?.[index] ?? null\n const last = index === to - 1\n drawn.push(\n <Fragment key={index}>\n {/* One parse puts a line break between top-level blocks; so does this, so the text of\n the page (and a copy of it) is the same either way. */}\n {index > 0 ? '\\n' : null}\n <Block\n source={block.source}\n closer={last ? closer : ''}\n offset={block.offset}\n growing={last && growing}\n headingIds={own && own.size > 0 ? own : null}\n headingKey={headingKeyOf(own)}\n options={options}\n />\n </Fragment>,\n )\n }\n return drawn\n },\n (before, after) => {\n if (\n before.from !== after.from ||\n before.to !== after.to ||\n before.closer !== after.closer ||\n before.growing !== after.growing ||\n before.options !== after.options ||\n (before.ids === null) !== (after.ids === null)\n )\n return false\n // From the end: in the group a reply is growing in, the block that changed is the last one.\n for (let index = after.to - 1; index >= after.from; index--) {\n const was = before.blocks[index]!\n const is = after.blocks[index]!\n // The splitter hands back a block that did not change as the same object.\n if (was !== is && (was.source !== is.source || was.offset !== is.offset)) return false\n if (after.ids) {\n const had = before.ids![index]\n const has = after.ids[index]\n // Most blocks have no heading: two empty maps are not spelled out to be compared.\n if (had !== has && ((had?.size ?? 0) > 0 || (has?.size ?? 0) > 0)) {\n if (headingKeyOf(had) !== headingKeyOf(has)) return false\n }\n }\n }\n return true\n },\n)\n\n/**\n * Markdown with the flowdown dialect: GitHub Markdown, callouts, steps, tabs, stats, pros and cons,\n * figures, file trees and, when the page supplies them, diagrams, formulas and highlighted code.\n *\n * The document is drawn as its top-level blocks, each remembered by its text: while a reply\n * streams, the blocks above its end do not change, and only the last one is parsed and drawn\n * again on each token.\n */\nexport function Markdown({\n children,\n streaming: arriving = false,\n smooth = false,\n caret: caretOption,\n components,\n extensions = NO_EXTENSIONS,\n headingIds = false,\n html = true,\n math: mathOption = null,\n mermaid = null,\n highlight = null,\n labels,\n urlTransform,\n className,\n unstyled = false,\n}: MarkdownProps): ReactNode {\n // `\\r\\n` from a Windows file or a server that sends it reads as `\\n` everywhere below.\n const whole = normalizeLineEndings(children)\n // Paced, the reply is still \"streaming\" until all of it is shown, a moment after it ended.\n const [text, behind] = useSmoothText(\n whole,\n arriving && smooth !== false,\n typeof smooth === 'number' ? smooth : DEFAULT_CATCH_UP_MS,\n )\n const streaming = arriving || behind\n const caret = caretOption ?? streaming\n // One document per `<Markdown>`: it completes the unfinished end of a streaming reply, cuts the\n // text into blocks and remembers them, so on the next token only the end of the reply is read.\n // Blocks that did not change come back as the same objects.\n const [doc] = useState(createDocument)\n const {\n blocks,\n closer,\n growing: endsInFence,\n headingIds: ids,\n } = doc.update(text, { streaming, math: Boolean(mathOption), headingIds })\n\n const math = useStage(mathOption, mathOption !== null && usesMath(blocks))\n // Whether the document has HTML is decided for the whole of it, not per block: a tag still\n // being typed (`<div`) is its own block, and only the whole document says it is HTML.\n const htmlStage = useStage(\n html === true ? loadHtml : html === false ? null : html,\n html !== false && usesHtml(blocks),\n )\n // A name outside the common ones shows as written until the whole table is there.\n const entities = useStage(loadEntities, !hasAllEntities() && usesRareEntity(blocks))\n const labelValue = useMemo(() => mergeLabels(labels), [labels])\n const options = useMemo<BlockOptions>(\n () => ({\n ...buildComponents(components),\n labels: labelValue,\n // Raw HTML is made into elements last, so every pass before it sees the text as written.\n extensions: [\n flowdownDialect,\n ...(math ? [math] : []),\n ...extensions,\n ...(htmlStage ? [htmlStage] : []),\n ],\n urlTransform: urlTransform ?? defaultUrlTransform,\n ownCode: Boolean(components?.pre || components?.code),\n ownTable: Boolean(\n components?.table || components?.thead || components?.tbody || extensions.length,\n ),\n }),\n // `entities` is here so that blocks are read again once every name can be decoded.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n [htmlStage, math, urlTransform, components, extensions, entities, labelValue],\n )\n const context = useMemo<RenderContext>(() => {\n if (mermaid) setMermaidLoader(mermaid)\n return { highlight, mermaid: mermaid ? 'page' : null }\n }, [highlight, mermaid])\n const groups: ReactNode[] = []\n for (let from = 0; from < blocks.length; from += GROUP) {\n const to = Math.min(from + GROUP, blocks.length)\n const last = to === blocks.length\n groups.push(\n <BlockGroup\n key={from}\n blocks={blocks}\n ids={ids}\n from={from}\n to={to}\n closer={last ? closer : ''}\n growing={last && endsInFence}\n options={options}\n />,\n )\n }\n\n return (\n <LabelsContext.Provider value={labelValue}>\n <RenderContextValue.Provider value={context}>\n <div\n className={unstyled ? className : className ? `flowdown ${className}` : 'flowdown'}\n data-streaming={streaming || undefined}\n data-caret={(streaming && caret) || undefined}\n >\n {groups}\n </div>\n </RenderContextValue.Provider>\n </LabelsContext.Provider>\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState } from "react";
|
|
3
|
+
// src/react/scrolls.ts
|
|
4
|
+
/**
|
|
5
|
+
* Whether a box scrolls sideways: a table's box, or the `<pre>` of a code block.
|
|
6
|
+
*
|
|
7
|
+
* A box that scrolls is a named group the Tab key stops on, so the arrow keys can scroll it:
|
|
8
|
+
* without that what is past the edge cannot be reached from the keyboard at all (WCAG 2.1.1). A box
|
|
9
|
+
* whose content fits stays out of the Tab order. It is measured again whenever it, or the `inner`
|
|
10
|
+
* element that holds its content, changes size.
|
|
11
|
+
*
|
|
12
|
+
* A line of code getting longer resizes neither, so the caller says when its text has changed
|
|
13
|
+
* (`again`): the box is then measured once in the next frame, when the page is laid out anyway. A
|
|
14
|
+
* measuring per token would lay the page out per token.
|
|
15
|
+
*
|
|
16
|
+
* Two hooks and no effect: a code block is drawn again on every token of its text, and an update
|
|
17
|
+
* there is short enough that each hook a component calls shows in the stream's whole time.
|
|
18
|
+
*/
|
|
19
|
+
function useScrolls(inner) {
|
|
20
|
+
const [scrolls, setScrolls] = useState(false);
|
|
21
|
+
const [box] = useState(() => watch(setScrolls, inner));
|
|
22
|
+
return [
|
|
23
|
+
box.attach,
|
|
24
|
+
scrolls,
|
|
25
|
+
box.again
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
/** The watching of one box, from React attaching it (`attach(element)`) to its leaving (`attach(null)`). */
|
|
29
|
+
function watch(tell, inner) {
|
|
30
|
+
let box = null;
|
|
31
|
+
let observer = null;
|
|
32
|
+
let frame = 0;
|
|
33
|
+
const measure = () => {
|
|
34
|
+
if (box) tell(box.scrollWidth > box.clientWidth + 1);
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
attach(element) {
|
|
38
|
+
if (frame) cancelAnimationFrame(frame);
|
|
39
|
+
frame = 0;
|
|
40
|
+
observer?.disconnect();
|
|
41
|
+
observer = null;
|
|
42
|
+
box = element;
|
|
43
|
+
if (!element) return;
|
|
44
|
+
measure();
|
|
45
|
+
if (typeof ResizeObserver === "undefined") return;
|
|
46
|
+
observer = new ResizeObserver(measure);
|
|
47
|
+
observer.observe(element);
|
|
48
|
+
const content = inner ? element.querySelector(inner) : null;
|
|
49
|
+
if (content) observer.observe(content);
|
|
50
|
+
},
|
|
51
|
+
again() {
|
|
52
|
+
if (!box || typeof requestAnimationFrame !== "function") return;
|
|
53
|
+
frame ||= requestAnimationFrame(() => {
|
|
54
|
+
frame = 0;
|
|
55
|
+
measure();
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
export { useScrolls };
|
|
61
|
+
|
|
62
|
+
//# sourceMappingURL=scrolls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scrolls.js","names":[],"sources":["../../src/react/scrolls.ts"],"sourcesContent":["'use client'\n\nimport { useState } from 'react'\n\n/**\n * Whether a box scrolls sideways: a table's box, or the `<pre>` of a code block.\n *\n * A box that scrolls is a named group the Tab key stops on, so the arrow keys can scroll it:\n * without that what is past the edge cannot be reached from the keyboard at all (WCAG 2.1.1). A box\n * whose content fits stays out of the Tab order. It is measured again whenever it, or the `inner`\n * element that holds its content, changes size.\n *\n * A line of code getting longer resizes neither, so the caller says when its text has changed\n * (`again`): the box is then measured once in the next frame, when the page is laid out anyway. A\n * measuring per token would lay the page out per token.\n *\n * Two hooks and no effect: a code block is drawn again on every token of its text, and an update\n * there is short enough that each hook a component calls shows in the stream's whole time.\n */\nexport function useScrolls<T extends HTMLElement>(\n inner?: string,\n): [attach: (element: T | null) => void, scrolls: boolean, again: () => void] {\n const [scrolls, setScrolls] = useState(false)\n const [box] = useState(() => watch<T>(setScrolls, inner))\n return [box.attach, scrolls, box.again]\n}\n\n/** The watching of one box, from React attaching it (`attach(element)`) to its leaving (`attach(null)`). */\nfunction watch<T extends HTMLElement>(tell: (scrolls: boolean) => void, inner: string | undefined) {\n let box: T | null = null\n let observer: ResizeObserver | null = null\n let frame = 0\n // One pixel of slack: a subpixel content width rounds up past the box it fits in.\n const measure = () => {\n if (box) tell(box.scrollWidth > box.clientWidth + 1)\n }\n return {\n attach(element: T | null): void {\n if (frame) cancelAnimationFrame(frame)\n frame = 0\n observer?.disconnect()\n observer = null\n box = element\n if (!element) return\n measure()\n if (typeof ResizeObserver === 'undefined') return\n observer = new ResizeObserver(measure)\n observer.observe(element)\n const content = inner ? element.querySelector(inner) : null\n if (content) observer.observe(content)\n },\n again(): void {\n if (!box || typeof requestAnimationFrame !== 'function') return\n frame ||= requestAnimationFrame(() => {\n frame = 0\n measure()\n })\n },\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAmBA,SAAgB,WACd,OAC4E;CAC5E,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,OAAO,eAAe,MAAS,YAAY,KAAK,CAAC;CACxD,OAAO;EAAC,IAAI;EAAQ;EAAS,IAAI;CAAK;AACxC;;AAGA,SAAS,MAA6B,MAAkC,OAA2B;CACjG,IAAI,MAAgB;CACpB,IAAI,WAAkC;CACtC,IAAI,QAAQ;CAEZ,MAAM,gBAAgB;EACpB,IAAI,KAAK,KAAK,IAAI,cAAc,IAAI,cAAc,CAAC;CACrD;CACA,OAAO;EACL,OAAO,SAAyB;GAC9B,IAAI,OAAO,qBAAqB,KAAK;GACrC,QAAQ;GACR,UAAU,WAAW;GACrB,WAAW;GACX,MAAM;GACN,IAAI,CAAC,SAAS;GACd,QAAQ;GACR,IAAI,OAAO,mBAAmB,aAAa;GAC3C,WAAW,IAAI,eAAe,OAAO;GACrC,SAAS,QAAQ,OAAO;GACxB,MAAM,UAAU,QAAQ,QAAQ,cAAc,KAAK,IAAI;GACvD,IAAI,SAAS,SAAS,QAAQ,OAAO;EACvC;EACA,QAAc;GACZ,IAAI,CAAC,OAAO,OAAO,0BAA0B,YAAY;GACzD,UAAU,4BAA4B;IACpC,QAAQ;IACR,QAAQ;GACV,CAAC;EACH;CACF;AACF"}
|
package/dist/react/table.js
CHANGED
|
@@ -2,34 +2,20 @@
|
|
|
2
2
|
import { table } from "../render/views.js";
|
|
3
3
|
import { useLabels } from "./labels.js";
|
|
4
4
|
import { react } from "./render.js";
|
|
5
|
-
import {
|
|
5
|
+
import { useScrolls } from "./scrolls.js";
|
|
6
6
|
// src/react/table.tsx
|
|
7
7
|
/**
|
|
8
8
|
* A table in a box of its own that scrolls sideways: a wide comparison then scrolls inside the
|
|
9
9
|
* reply instead of widening the page, which on a phone pushed every line of text off screen.
|
|
10
10
|
*
|
|
11
|
-
* A box that scrolls is a
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* size: a reply that streams a row in, or a window made narrower.
|
|
11
|
+
* A box that scrolls is a Tab stop, and one that fits is not: see `useScrolls`. The box is measured
|
|
12
|
+
* again whenever it or the table changes size: a reply that streams a row in, or a window made
|
|
13
|
+
* narrower.
|
|
15
14
|
*/
|
|
16
15
|
function Table({ children, ...props }) {
|
|
17
16
|
const labels = useLabels();
|
|
18
|
-
const [
|
|
19
|
-
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
const element = box;
|
|
22
|
-
if (!element) return;
|
|
23
|
-
const measure = () => setScrolls(element.scrollWidth > element.clientWidth + 1);
|
|
24
|
-
measure();
|
|
25
|
-
if (typeof ResizeObserver === "undefined") return;
|
|
26
|
-
const observer = new ResizeObserver(measure);
|
|
27
|
-
observer.observe(element);
|
|
28
|
-
const inner = element.querySelector("table");
|
|
29
|
-
if (inner) observer.observe(inner);
|
|
30
|
-
return () => observer.disconnect();
|
|
31
|
-
}, [box]);
|
|
32
|
-
return table(react, props, children === void 0 ? [] : [children], scrolls, labels, { ref: setBox });
|
|
17
|
+
const [attach, scrolls] = useScrolls("table");
|
|
18
|
+
return table(react, props, children === void 0 ? [] : [children], scrolls, labels, { ref: attach });
|
|
33
19
|
}
|
|
34
20
|
export { Table };
|
|
35
21
|
|
package/dist/react/table.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"table.js","names":[],"sources":["../../src/react/table.tsx"],"sourcesContent":["'use client'\n\nimport {
|
|
1
|
+
{"version":3,"file":"table.js","names":[],"sources":["../../src/react/table.tsx"],"sourcesContent":["'use client'\n\nimport type { ComponentPropsWithoutRef } from 'react'\nimport type { Props } from '../render/factory'\nimport { table } from '../render/views'\nimport { useLabels } from './labels'\nimport { react } from './render'\nimport { useScrolls } from './scrolls'\n\n/**\n * A table in a box of its own that scrolls sideways: a wide comparison then scrolls inside the\n * reply instead of widening the page, which on a phone pushed every line of text off screen.\n *\n * A box that scrolls is a Tab stop, and one that fits is not: see `useScrolls`. The box is measured\n * again whenever it or the table changes size: a reply that streams a row in, or a window made\n * narrower.\n */\nexport function Table({\n children,\n ...props\n}: ComponentPropsWithoutRef<'table'> & { node?: unknown }) {\n const labels = useLabels()\n const [attach, scrolls] = useScrolls<HTMLDivElement>('table')\n return table(react, props as Props, children === undefined ? [] : [children], scrolls, labels, {\n ref: attach,\n })\n}\n"],"mappings":";;;;;;;;;;;;;;AAiBA,SAAgB,MAAM,EACpB,UACA,GAAG,SACsD;CACzD,MAAM,SAAS,UAAU;CACzB,MAAM,CAAC,QAAQ,WAAW,WAA2B,OAAO;CAC5D,OAAO,MAAM,OAAO,OAAgB,aAAa,KAAA,IAAY,CAAC,IAAI,CAAC,QAAQ,GAAG,SAAS,QAAQ,EAC7F,KAAK,OACP,CAAC;AACH"}
|