1import React from "react";
2import Link from "next/link";
3import Markdoc, { Ast, Tag } from "@markdoc/markdoc";
4import { heading } from "./schema/Heading.markdoc";
5import { footnoteRef } from "./schema/footnoteRef.markdoc";
6import { footnoteItem } from "./schema/footnoteItem.markdoc";
7import { link } from "./schema/link.markdoc";
8import { image } from "./schema/image.markdoc";
9import { sup } from "./schema/superscript.markdoc";
10import Tabs from "./content/Tabs";
11import Tab from "./content/Tab";
12import Button from "./content/Button";
13import Callout from "./content/Callout";
14import Highlight, { defaultProps } from "prism-react-renderer";
15import parse from "html-react-parser";
16import Prism from "prism-react-renderer/prism";
17(typeof global !== "undefined" ? global : window).Prism = Prism;
18require("prismjs/components/prism-hoon");
20export function Fence({ children, language }) {
29 {({ className, style, tokens, getLineProps, getTokenProps }) => (
30 <pre className={className} style={style}>
31 {tokens.slice(0, -1).map((line, i) => (
32 <div {...getLineProps({ line, key: i })}>
33 {line.map((token, key) => (
34 <span {...getTokenProps({ token, key })} />
50 "The programming language of the code block. Place it after the backticks.",
67 transform(node, config) {
69 .transformChildren(config)
70 .filter((child) => child && child.name === "Tab")
71 .map((tab) => (typeof tab === "object" ? tab.attributes.label : null));
73 return new Tag(this.render, { labels }, node.transformChildren(config));
102 render: "CustomFence",
107 render: "RenderHtml",
114 return node.attributes.content
115 ? new Tag("RenderHtml", { content: node.attributes.content }, [
122const RenderHtml = ({ content }) => {
123 return parse(content);
126const superscript = ({ children }) => <sup>{children}</sup>;
128const CustomFence = ({ children }) => <pre>{children}</pre>;
130const NextLink = ({ href, target, children }) => {
132 <Link href={href} passHref>
133 <a target={target}>{children}</a>
138export function MarkdownParse({ post }) {
139 const tokeniser = new Markdoc.Tokenizer({ html: true, linkify: true });
140 const tokens = tokeniser.tokenize(post.content);
141 const ast = Markdoc.parse(tokens);
142 const finalAst = footnoteParse(ast);
143 return Markdoc.transform(finalAst, {
167export default function Markdown({ content }) {
168 return Markdoc.renderers.react(content, React, {
182const footnoteParse = (partialAst) => {
183 const endNotePattern = /\[\^(\d+)\]:\s/m;
184 const inlineFnPattern = /\[\^(\d+)\](?!:)/gm;
186 function* getFootnoteItemNodes(nodes) {
188 let itemsProcessed = 0;
189 for (const n of nodes) {
191 if (n.type !== "softbreak") results.push(n);
192 if (n.type === "softbreak" || itemsProcessed === nodes.length) {
199 function findFootnoteContainerNode(ast) {
200 const generator = ast.walk();
203 for (const node of generator) {
205 node.attributes.content &&
206 endNotePattern.test(node.attributes.content)
211 if (node.type === "inline") container = node;
213 return match ? container : undefined;
216 function processFootnotes(ast) {
217 // Get a refrence to the node containing endNotes; if not present, early return
218 const fnContainerNode = findFootnoteContainerNode(ast);
219 if (!fnContainerNode) return;
220 // We have footnotes, so create a new list node which will contain the list of endNotes
221 const fnList = new Ast.Node("list", {
225 // Get the children nodes for each footnote item
226 const fnItems = getFootnoteItemNodes(fnContainerNode.children);
227 for (const fn of fnItems) {
228 const token = endNotePattern.exec(fn[0].attributes.content);
230 // Remove the markdown footnote syntax (e.g. [^1]) from the string
231 fn[0].attributes.content = fn[0].attributes.content.replace(
235 // Create a new footnote item and append to the fnList
237 const fnItem = new Ast.Node(
248 ast.children.pop(); // remove the last paragraph in the doc being replaced by the fnList
252 function processFootnoteRefs(ast) {
254 for (const node of ast.walk()) {
255 if (node.attributes.content) {
256 // Check if there's a footnote ref token
257 const token = inlineFnPattern.exec(node.attributes.content);
259 // Break the string where the foonote ref is, assign first part of string to the current node content
260 const [prevText, nextText] = node.attributes.content.split(token[0]);
261 node.attributes.content = prevText;
263 // Create a footnote node and insert it in the tree
265 const fn = new Ast.Node("footnoteRef", {
272 // Create a text node for the text which follows after the footnote and insert it in the tree
273 const next = new Ast.Node("text", { content: nextText });
277 // If the node is of inline type, update parent
278 if (node.type == "inline") parent = node;
282 processFootnoteRefs(partialAst);
283 processFootnotes(partialAst);
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list