myst-to-react 0.1.14
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 +6 -0
- package/package.json +48 -0
- package/src/admonitions.tsx +183 -0
- package/src/basic.tsx +217 -0
- package/src/cite.tsx +94 -0
- package/src/code.tsx +119 -0
- package/src/components/ClickPopover.tsx +56 -0
- package/src/components/CopyIcon.tsx +40 -0
- package/src/components/HoverPopover.tsx +60 -0
- package/src/components/LinkCard.tsx +42 -0
- package/src/convertToReact.ts +33 -0
- package/src/crossReference.tsx +139 -0
- package/src/extensions/chemicalFormula.tsx +42 -0
- package/src/extensions/index.tsx +10 -0
- package/src/extensions/siunits.tsx +15 -0
- package/src/footnotes.tsx +30 -0
- package/src/heading.tsx +68 -0
- package/src/iframe.tsx +42 -0
- package/src/image.tsx +72 -0
- package/src/index.tsx +59 -0
- package/src/inlineError.tsx +15 -0
- package/src/links/index.tsx +132 -0
- package/src/links/rrid.tsx +81 -0
- package/src/links/wiki.tsx +119 -0
- package/src/math.tsx +81 -0
- package/src/mermaid.tsx +49 -0
- package/src/myst.tsx +185 -0
- package/src/output/components.tsx +34 -0
- package/src/output/error.tsx +20 -0
- package/src/output/hooks.ts +127 -0
- package/src/output/index.tsx +7 -0
- package/src/output/jupyter.tsx +86 -0
- package/src/output/output.tsx +79 -0
- package/src/output/outputBlock.tsx +21 -0
- package/src/output/safe.tsx +84 -0
- package/src/output/selectors.ts +15 -0
- package/src/output/stream.tsx +18 -0
- package/src/reactive.tsx +64 -0
- package/src/tabs.tsx +58 -0
- package/src/types.ts +6 -0
package/src/index.tsx
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { NodeRenderer } from './types';
|
|
2
|
+
import type { GenericParent } from 'mystjs';
|
|
3
|
+
import { mystToReact } from './convertToReact';
|
|
4
|
+
import BASIC_RENDERERS from './basic';
|
|
5
|
+
import ADMONITION_RENDERERS from './admonitions';
|
|
6
|
+
import CITE_RENDERERS from './cite';
|
|
7
|
+
import FOOTNOTE_RENDERERS from './footnotes';
|
|
8
|
+
import CODE_RENDERERS from './code';
|
|
9
|
+
import MATH_RENDERERS from './math';
|
|
10
|
+
import REACTIVE_RENDERERS from './reactive';
|
|
11
|
+
import IFRAME_RENDERERS from './iframe';
|
|
12
|
+
import IMAGE_RENDERERS from './image';
|
|
13
|
+
import LINK_RENDERERS from './links';
|
|
14
|
+
import OUTPUT_RENDERERS from './output';
|
|
15
|
+
import HEADING_RENDERERS from './heading';
|
|
16
|
+
import CROSS_REFERENCE_RENDERERS from './crossReference';
|
|
17
|
+
import TAB_RENDERERS from './tabs';
|
|
18
|
+
import MYST_RENDERERS from './myst';
|
|
19
|
+
import MERMAID_RENDERERS from './mermaid';
|
|
20
|
+
export { MySTRenderer } from './myst';
|
|
21
|
+
import EXT_RENDERERS from './extensions';
|
|
22
|
+
|
|
23
|
+
export type { NodeRenderer } from './types';
|
|
24
|
+
export { Bibliography } from './cite';
|
|
25
|
+
export { CopyIcon } from './components/CopyIcon';
|
|
26
|
+
|
|
27
|
+
export const DEFAULT_RENDERERS: Record<string, NodeRenderer> = {
|
|
28
|
+
...BASIC_RENDERERS,
|
|
29
|
+
...IMAGE_RENDERERS,
|
|
30
|
+
...LINK_RENDERERS,
|
|
31
|
+
...CODE_RENDERERS,
|
|
32
|
+
...MATH_RENDERERS,
|
|
33
|
+
...CITE_RENDERERS,
|
|
34
|
+
...TAB_RENDERERS,
|
|
35
|
+
...IFRAME_RENDERERS,
|
|
36
|
+
...FOOTNOTE_RENDERERS,
|
|
37
|
+
...ADMONITION_RENDERERS,
|
|
38
|
+
...REACTIVE_RENDERERS,
|
|
39
|
+
...OUTPUT_RENDERERS,
|
|
40
|
+
...HEADING_RENDERERS,
|
|
41
|
+
...CROSS_REFERENCE_RENDERERS,
|
|
42
|
+
...MYST_RENDERERS,
|
|
43
|
+
...MERMAID_RENDERERS,
|
|
44
|
+
...EXT_RENDERERS,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export function useParse(
|
|
48
|
+
node: GenericParent | null,
|
|
49
|
+
renderers: Record<string, NodeRenderer> = DEFAULT_RENDERERS,
|
|
50
|
+
) {
|
|
51
|
+
if (!node) return null;
|
|
52
|
+
try {
|
|
53
|
+
const nodes = mystToReact(node, renderers);
|
|
54
|
+
return nodes;
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error(error);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ExclamationIcon } from '@heroicons/react/outline';
|
|
2
|
+
|
|
3
|
+
interface Props {
|
|
4
|
+
value: string;
|
|
5
|
+
message?: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function InlineError({ value, message }: Props) {
|
|
9
|
+
return (
|
|
10
|
+
<span className="text-yellow-600" title={message || value}>
|
|
11
|
+
<ExclamationIcon className="inline h-[1em] mr-1" />
|
|
12
|
+
{value}
|
|
13
|
+
</span>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import type { Link } from 'myst-spec';
|
|
2
|
+
import { Link as RemixLink } from '@remix-run/react';
|
|
3
|
+
import { ExternalLinkIcon, LinkIcon } from '@heroicons/react/outline';
|
|
4
|
+
import { useSiteManifest } from '@curvenote/ui-providers';
|
|
5
|
+
import type { ManifestProjectPage, SiteManifest } from '@curvenote/site-common';
|
|
6
|
+
import type { NodeRenderer } from '../types';
|
|
7
|
+
import { HoverPopover } from '../components/HoverPopover';
|
|
8
|
+
import { LinkCard } from '../components/LinkCard';
|
|
9
|
+
import { WikiLink } from './wiki';
|
|
10
|
+
import { RRIDLink } from './rrid';
|
|
11
|
+
|
|
12
|
+
type TransformedLink = Link & { internal?: boolean; protocol?: string };
|
|
13
|
+
|
|
14
|
+
function getPageInfo(
|
|
15
|
+
site: SiteManifest | undefined,
|
|
16
|
+
path: string,
|
|
17
|
+
): ManifestProjectPage | undefined {
|
|
18
|
+
if (!site) return undefined;
|
|
19
|
+
const [projectSlug, pageSlug] = path.replace(/^\//, '').split('/');
|
|
20
|
+
const project = site.projects.find((p) => p.slug === projectSlug);
|
|
21
|
+
if (!project) return undefined;
|
|
22
|
+
return project.pages.find(
|
|
23
|
+
(p) => (p as ManifestProjectPage).slug === pageSlug,
|
|
24
|
+
) as ManifestProjectPage;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function InternalLink({ url, children }: { url: string; children: React.ReactNode }) {
|
|
28
|
+
const site = useSiteManifest();
|
|
29
|
+
const page = getPageInfo(site, url);
|
|
30
|
+
const skipPreview = !page || (!page.description && !page.thumbnail);
|
|
31
|
+
if (!page || skipPreview) {
|
|
32
|
+
return (
|
|
33
|
+
<RemixLink to={url} prefetch="intent">
|
|
34
|
+
{children}
|
|
35
|
+
</RemixLink>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return (
|
|
39
|
+
<HoverPopover
|
|
40
|
+
card={
|
|
41
|
+
<LinkCard
|
|
42
|
+
internal
|
|
43
|
+
url={url}
|
|
44
|
+
title={page.title}
|
|
45
|
+
description={page.description}
|
|
46
|
+
thumbnail={page.thumbnailOptimized || page.thumbnail}
|
|
47
|
+
/>
|
|
48
|
+
}
|
|
49
|
+
>
|
|
50
|
+
<RemixLink to={url} prefetch="intent">
|
|
51
|
+
{children}
|
|
52
|
+
</RemixLink>
|
|
53
|
+
</HoverPopover>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export const link: NodeRenderer<TransformedLink> = (node, children) => {
|
|
58
|
+
const internal = node.internal ?? false;
|
|
59
|
+
const protocol = node.protocol;
|
|
60
|
+
|
|
61
|
+
switch (protocol) {
|
|
62
|
+
case 'wiki':
|
|
63
|
+
return (
|
|
64
|
+
<WikiLink
|
|
65
|
+
key={node.key}
|
|
66
|
+
url={node.url}
|
|
67
|
+
page={node.data?.page as string}
|
|
68
|
+
wiki={node.data?.wiki as string}
|
|
69
|
+
>
|
|
70
|
+
{children}
|
|
71
|
+
</WikiLink>
|
|
72
|
+
);
|
|
73
|
+
case 'rrid':
|
|
74
|
+
return <RRIDLink key={node.key} rrid={node.data?.rrid as string} />;
|
|
75
|
+
default:
|
|
76
|
+
if (internal) {
|
|
77
|
+
return (
|
|
78
|
+
<InternalLink key={node.key} url={node.url}>
|
|
79
|
+
{children}
|
|
80
|
+
</InternalLink>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return (
|
|
84
|
+
<a key={node.key} target="_blank" href={node.url} rel="noreferrer">
|
|
85
|
+
{children}
|
|
86
|
+
</a>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const linkBlock: NodeRenderer<TransformedLink> = (node, children) => {
|
|
92
|
+
const iconClass = 'w-6 h-6 self-center transition-transform flex-none ml-3';
|
|
93
|
+
const containerClass =
|
|
94
|
+
'flex-1 p-4 my-4 block border font-normal hover:border-blue-500 dark:hover:border-blue-400 no-underline hover:text-blue-600 dark:hover:text-blue-400 text-gray-600 dark:text-gray-100 border-gray-200 dark:border-gray-500 rounded shadow-sm hover:shadow-lg dark:shadow-neutral-700';
|
|
95
|
+
const internal = node.internal ?? false;
|
|
96
|
+
const nested = (
|
|
97
|
+
<div className="flex align-middle h-full">
|
|
98
|
+
<div className="flex-grow">
|
|
99
|
+
{node.title}
|
|
100
|
+
<div className="text-xs text-gray-500 dark:text-gray-400">{children}</div>
|
|
101
|
+
</div>
|
|
102
|
+
{internal && <LinkIcon className={iconClass} />}
|
|
103
|
+
{!internal && <ExternalLinkIcon className={iconClass} />}
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (internal) {
|
|
108
|
+
return (
|
|
109
|
+
<RemixLink key={node.key} to={node.url} prefetch="intent" className={containerClass}>
|
|
110
|
+
{nested}
|
|
111
|
+
</RemixLink>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
return (
|
|
115
|
+
<a
|
|
116
|
+
key={node.key}
|
|
117
|
+
className={containerClass}
|
|
118
|
+
target="_blank"
|
|
119
|
+
rel="noopener noreferrer"
|
|
120
|
+
href={node.url}
|
|
121
|
+
>
|
|
122
|
+
{nested}
|
|
123
|
+
</a>
|
|
124
|
+
);
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const LINK_RENDERERS = {
|
|
128
|
+
link,
|
|
129
|
+
linkBlock,
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export default LINK_RENDERERS;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import useSWR from 'swr';
|
|
2
|
+
import { ExternalLinkIcon } from '@heroicons/react/outline';
|
|
3
|
+
import { ClickPopover } from '../components/ClickPopover';
|
|
4
|
+
|
|
5
|
+
const fetcher = (...args: Parameters<typeof fetch>) =>
|
|
6
|
+
fetch(...args).then((res) => {
|
|
7
|
+
if (res.status === 200) return res.json();
|
|
8
|
+
throw new Error(`Content returned with status ${res.status}.`);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
function RRIDChild({ rrid }: { rrid: string }) {
|
|
12
|
+
const { data, error } = useSWR(`https://scicrunch.org/resolver/${rrid}.json`, fetcher);
|
|
13
|
+
if (!data && !error) {
|
|
14
|
+
return <span className="animate-pulse">Loading...</span>;
|
|
15
|
+
}
|
|
16
|
+
const hit = data?.hits?.hits?.[0];
|
|
17
|
+
if (error || !hit) {
|
|
18
|
+
return <span>Error loading {rrid}.</span>;
|
|
19
|
+
}
|
|
20
|
+
const {
|
|
21
|
+
name: title,
|
|
22
|
+
curie,
|
|
23
|
+
description,
|
|
24
|
+
supercategory,
|
|
25
|
+
keywords,
|
|
26
|
+
types: categories,
|
|
27
|
+
} = hit?._source?.item ?? {};
|
|
28
|
+
const category = supercategory?.[0]?.name;
|
|
29
|
+
const types = (categories?.map(({ name }: { name: string }) => name) as string[]) ?? [];
|
|
30
|
+
const tags = (keywords?.map(({ keyword }: { keyword: string }) => keyword) as string[]) ?? [];
|
|
31
|
+
return (
|
|
32
|
+
<div>
|
|
33
|
+
<a
|
|
34
|
+
href={`https://scicrunch.org/resolver/${rrid}`}
|
|
35
|
+
target="_blank"
|
|
36
|
+
rel="noopener noreferrer"
|
|
37
|
+
className="absolute top-4 right-1 opacity-70 hover:opacity-100"
|
|
38
|
+
>
|
|
39
|
+
<ExternalLinkIcon className="w-6 h-6" />
|
|
40
|
+
</a>
|
|
41
|
+
<p className="text-sm font-light">RRID: {category}</p>
|
|
42
|
+
<div className="text-xl font-bold mb-4">
|
|
43
|
+
{title} <code>{curie}</code>
|
|
44
|
+
</div>
|
|
45
|
+
<p className="text-md">{description}</p>
|
|
46
|
+
{types.length > 0 && (
|
|
47
|
+
<>
|
|
48
|
+
<div className="text-xs font-thin my-2">Categories</div>
|
|
49
|
+
<div className="flex flex-wrap ml-1">
|
|
50
|
+
{types?.map((tag) => (
|
|
51
|
+
<span className="ml-1 text-xs inline-flex items-center uppercase px-3 py-1 rounded-full border">
|
|
52
|
+
{tag}
|
|
53
|
+
</span>
|
|
54
|
+
))}
|
|
55
|
+
</div>
|
|
56
|
+
</>
|
|
57
|
+
)}
|
|
58
|
+
{tags.length > 0 && (
|
|
59
|
+
<>
|
|
60
|
+
<div className="text-xs font-thin my-2">Tags</div>
|
|
61
|
+
<div className="flex flex-wrap ml-1">
|
|
62
|
+
{tags?.map((tag) => (
|
|
63
|
+
<span className="ml-1 text-xs inline-flex items-center uppercase px-3 py-1 rounded-full border">
|
|
64
|
+
{tag}
|
|
65
|
+
</span>
|
|
66
|
+
))}
|
|
67
|
+
</div>
|
|
68
|
+
</>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function RRIDLink({ rrid }: { rrid: string }) {
|
|
75
|
+
return (
|
|
76
|
+
<ClickPopover card={<RRIDChild rrid={rrid} />}>
|
|
77
|
+
<span>RRID: </span>
|
|
78
|
+
<cite className="italic">{rrid}</cite>
|
|
79
|
+
</ClickPopover>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import useSWR from 'swr';
|
|
2
|
+
import { ExternalLinkIcon } from '@heroicons/react/outline';
|
|
3
|
+
import { HoverPopover } from '../components/HoverPopover';
|
|
4
|
+
import { LinkCard } from '../components/LinkCard';
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
const WikiTextMark = () => (
|
|
8
|
+
<svg
|
|
9
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
10
|
+
version="1.1"
|
|
11
|
+
id="svg3400"
|
|
12
|
+
viewBox="0 0 500 85"
|
|
13
|
+
className="h-4"
|
|
14
|
+
>
|
|
15
|
+
<g transform="translate(-357.71336,-784.65111)">
|
|
16
|
+
<path
|
|
17
|
+
fill="currentColor"
|
|
18
|
+
d="m 412.98736,806.72288 c 0,0 -1.468,-2.984 -1.968,-3.976 -3.336,-6.612 -3.264,-6.964 -6.636,-7.408 -0.944,-0.124 -1.432,-0.244 -1.432,-0.712 v -2.092 l 0.284,-0.204 c 6.092,-0.008 19.648,-0.008 19.648,-0.008 l 0.52,0.172 v 2.084 c 0,0.476 -0.34,0.72 -1.032,0.72 l -1.424,0.204 c -3.6,0.28 -3.012,1.752 -0.62,6.516 l 22.708,46.46 0.776,0.212 20.16,-47.844 c 0.7,-1.928 0.592,-3.296 -0.308,-4.1 -0.904,-0.784 -1.54,-1.248 -3.876,-1.344 l -1.892,-0.096 c -0.232,0 -0.452,-0.084 -0.664,-0.24 -0.208,-0.148 -0.316,-0.344 -0.316,-0.58 v -1.992 l 0.288,-0.204 c 5.68,0 22.776,0 22.776,0 l 0.232,0.204 v 1.992 c 0,0.54 -0.308,0.812 -0.916,0.812 -2.984,0.132 -5.196,0.78 -6.628,1.936 -1.436,1.164 -2.556,2.768 -3.36,4.852 0,0 -18.54,42.436 -24.884,56.54 -2.428,4.624 -4.816,4.2 -6.916,-0.132 -4.436,-9.136 -16.928,-36.852 -16.928,-36.852 l -7.592,-14.92 z"
|
|
19
|
+
/>
|
|
20
|
+
<path
|
|
21
|
+
fill="currentColor"
|
|
22
|
+
d="m 449.37136,792.29888 c 0,0 -12.812,-0.04 -18.516,0 l -0.284,0.204 v 1.984 c 0,0.244 0.104,0.436 0.312,0.584 0.212,0.156 0.428,0.236 0.66,0.236 l 0.916,0.1 c 2.332,0.096 3.404,0.708 3.724,1.092 0.556,0.68 0.832,1.416 -0.516,4.356 l -23.92,47.708 -0.628,-0.164 c 0,0 -17.132,-36.124 -22.092,-48.208 -0.516,-1.252 -0.748,-2.18 -0.748,-2.752 0,-1.228 1.108,-1.892 3.324,-1.984 l 2.584,-0.108 c 0.684,0 1.032,-0.264 1.032,-0.812 v -1.984 l -0.232,-0.204 c 0,0 -20.52,-0.024 -24.76,0 l -0.232,0.204 v 2.084 c 0,0.376 0.476,0.608 1.432,0.712 2.604,0.14 4.316,0.556 5.14,1.248 0.82,0.7 1.688,2.468 2.84,5.224 6.228,14.96 19.46,41.656 25.904,56.86 1.856,4.152 4.188,4.796 7.02,-0.128 4.904,-9.096 18.2,-36.872 18.2,-36.872 l 10.472,-19.584 c 1.22,-2.068 2.396,-3.912 2.992,-4.772 1.092,-1.572 1.704,-1.868 4.688,-2.008 0.608,0 0.916,-0.272 0.916,-0.82 v -1.984 l -0.228,-0.212 z"
|
|
23
|
+
/>
|
|
24
|
+
<path
|
|
25
|
+
fill="currentColor"
|
|
26
|
+
d="m 488.32736,854.23088 c 0,2.288 1.04,3.64 4.368,3.952 l 2.184,0.212 c 0.312,0 0.624,0.204 0.624,0.62 v 2.184 l -0.104,0.212 c 0,0 -7.904,-0.212 -11.128,-0.212 -2.912,0 -11.024,0.212 -11.024,0.212 l -0.208,-0.212 v -2.184 c 0,-0.416 0.312,-0.62 0.728,-0.62 l 2.08,-0.212 c 3.432,-0.312 4.472,-1.976 4.472,-3.952 v -34.632 c 0,-2.284 -1.252,-2.916 -4.472,-3.22 l -2.08,-0.216 c -0.416,0 -0.728,-0.096 -0.728,-0.416 v -2.396 l 0.104,-0.208 c 0,0 8.216,0.208 11.44,0.208 2.808,0 10.816,-0.208 10.816,-0.208 l 0.104,0.208 v 2.288 c 0,0.312 -0.208,0.52 -0.624,0.52 l -2.184,0.212 c -3.224,0.308 -4.368,1.144 -4.368,3.228 v 34.632 z"
|
|
27
|
+
/>
|
|
28
|
+
<path
|
|
29
|
+
fill="currentColor"
|
|
30
|
+
d="m 519.01936,854.23088 c 0,2.288 1.352,3.432 4.368,3.952 l 1.144,0.212 c 0.312,0.1 0.624,0.204 0.624,0.62 v 2.184 l -0.104,0.212 c 0,0 -6.552,-0.212 -9.672,-0.212 -2.912,0 -11.44,0.212 -11.44,0.212 l -0.208,-0.212 v -2.184 c 0,-0.416 0.312,-0.62 0.728,-0.62 l 2.08,-0.212 c 3.12,-0.312 4.472,-1.976 4.472,-3.952 v -34.632 c 0,-2.284 -1.144,-2.916 -4.472,-3.22 l -2.08,-0.216 c -0.416,0 -0.728,-0.096 -0.728,-0.416 v -2.396 l 0.104,-0.208 c 0,0 8.528,0.208 11.752,0.208 2.916,0 9.464,-0.208 9.464,-0.208 l 0.104,0.208 v 2.288 c 0,0.312 -0.208,0.416 -0.624,0.52 l -1.144,0.212 c -3.224,0.62 -4.368,1.144 -4.368,3.228 v 15.704 c 3.432,0 5.408,-1.764 9.984,-6.656 4.16,-4.58 7.384,-8.42 7.384,-10.4 0,-0.836 -0.936,-1.768 -3.016,-2.084 -0.416,-0.1 -0.624,-0.208 -0.624,-0.52 v -2.396 l 0.104,-0.1 c 0,0 8.424,0.208 11.44,0.208 2.704,0 7.28,-0.208 7.28,-0.208 l 0.312,0.1 v 2.396 c 0,0.312 -0.312,0.52 -0.832,0.52 -4.056,0.312 -6.864,0.52 -10.608,4.784 l -8.944,10.292 c -1.768,1.876 -2.392,3.016 -2.392,3.852 0,0.624 0.416,1.14 0.936,1.764 l 15.704,18.928 c 1.248,1.452 3.744,2.08 5.824,2.492 l 0.52,0.1 c 0.312,0.104 0.728,0.312 0.728,0.624 v 2.184 l -0.312,0.104 c 0,0 -5.512,-0.104 -8.424,-0.104 -2.6,0 -6.344,0.104 -6.344,0.104 l -0.104,-0.104 v -1.248 c 0,-1.356 -0.104,-2.492 -1.56,-4.156 l -11.544,-14.356 c -1.56,-2.084 -3.224,-2.596 -5.512,-2.596 v 15.404 l 0,0 z"
|
|
31
|
+
/>
|
|
32
|
+
<path
|
|
33
|
+
fill="currentColor"
|
|
34
|
+
d="m 575.08736,854.23088 c 0,2.288 1.04,3.64 4.368,3.952 l 2.184,0.212 c 0.312,0 0.624,0.204 0.624,0.62 v 2.184 l -0.104,0.212 c 0,0 -7.904,-0.212 -11.128,-0.212 -2.912,0 -11.024,0.212 -11.024,0.212 l -0.208,-0.212 v -2.184 c 0,-0.416 0.312,-0.62 0.728,-0.62 l 2.08,-0.212 c 3.432,-0.312 4.472,-1.976 4.472,-3.952 v -34.632 c 0,-2.284 -1.248,-2.916 -4.472,-3.22 l -2.08,-0.216 c -0.416,0 -0.728,-0.096 -0.728,-0.416 v -2.396 l 0.104,-0.208 c 0,0 8.216,0.208 11.44,0.208 2.808,0 10.816,-0.208 10.816,-0.208 l 0.104,0.208 v 2.288 c 0,0.312 -0.208,0.52 -0.624,0.52 l -2.184,0.212 c -3.224,0.308 -4.368,1.144 -4.368,3.228 v 34.632 z"
|
|
35
|
+
/>
|
|
36
|
+
<path
|
|
37
|
+
fill="currentColor"
|
|
38
|
+
d="m 597.55936,819.80288 c 0,-3.224 -1.352,-3.328 -6.24,-3.64 -0.624,0 -0.728,-0.308 -0.728,-0.52 v -2.392 l 0.208,-0.104 c 1.56,0 7.696,0.208 10.712,0.208 2.912,0 7.072,-0.208 10.92,-0.208 14.76,0 17.464,7.276 17.464,12.692 0,6.244 -3.016,16.016 -17.776,16.016 -2.6,0 -4.68,-0.212 -6.556,-1.04 v 13.928 c 0,2.288 1.248,2.812 4.58,3.328 l 2.084,0.312 c 0.416,0.104 0.728,0.212 0.728,0.624 v 2.184 l -0.1,0.208 c 0,0 -8.32,-0.208 -11.544,-0.208 -2.912,0 -10.712,0.208 -10.712,0.208 l -0.104,-0.208 v -2.184 c 0,-0.416 0.208,-0.52 0.624,-0.624 l 2.184,-0.312 c 3.224,-0.416 4.264,-1.352 4.264,-3.328 l -0.008,-34.94 0,0 z m 8.008,18.1 c 2.08,0.836 4.68,0.936 6.76,0.936 6.244,0 9.248,-3.752 9.248,-11.752 0,-8.944 -4.056,-10.92 -9.768,-10.92 -4.996,0 -6.244,0.936 -6.244,3.64 l 0.004,18.096 0,0 z"
|
|
39
|
+
/>
|
|
40
|
+
<path
|
|
41
|
+
fill="currentColor"
|
|
42
|
+
d="m 647.80736,861.19888 c -3.12,0 -10.292,0.212 -10.292,0.212 l -0.108,-0.212 v -2.288 c 0,-0.204 0.108,-0.516 0.624,-0.516 l 2.396,-0.212 c 3.228,-0.312 4.164,-2.392 4.164,-4.468 v -34.32 c 0,-2.4 -1.252,-2.812 -4.164,-3.016 l -2.396,-0.216 c -0.624,-0.096 -0.624,-0.308 -0.624,-0.416 v -2.396 l 0.108,-0.208 c 0,0 8.108,0.208 11.228,0.208 h 15.288 c 3.12,0 10.508,-0.208 10.508,-0.208 l 0.212,0.208 c 0.1,4.472 0.828,8.536 1.56,12.484 l -2.596,0.416 c -1.14,-3.852 -3.116,-9.36 -9.772,-9.36 h -8.32 c -1.976,0 -3.016,0.728 -3.016,2.492 v 15.084 h 7.796 c 2.916,0 3.64,-1.248 4.06,-3.328 l 0.62,-2.184 c 0,-0.312 0.212,-0.524 0.624,-0.524 h 1.764 c 0.212,0 0.212,0.108 0.212,0.312 -0.212,2.288 -0.624,5.204 -0.624,7.7 0,2.596 0.416,4.784 0.624,7.176 l -0.212,0.212 h -1.764 c -0.516,0 -0.732,-0.212 -0.732,-0.524 l -0.52,-2.284 c -0.416,-2.084 -1.352,-3.228 -4.064,-3.228 h -7.792 v 15.916 c 0,1.972 1.136,3.948 3.016,3.948 h 9.88 c 5.304,0 8.22,-3.328 9.464,-8.74 l 2.596,0.312 -2.084,11.956 -0.208,0.208 c 0,0 -6.136,-0.208 -9.256,-0.208 l -18.2,0.012 0,0 z"
|
|
43
|
+
/>
|
|
44
|
+
<path
|
|
45
|
+
fill="currentColor"
|
|
46
|
+
d="m 686.91936,858.39488 2.392,-0.212 c 3.124,-0.312 4.164,-1.248 4.164,-4.468 v -34.32 c 0,-2.712 -0.736,-2.712 -4.164,-3.016 l -2.28,-0.216 c -0.416,0 -0.736,-0.208 -0.736,-0.52 v -2.288 l 0.1,-0.208 c 0,0 9.156,0.208 11.032,0.208 1.968,0 11.024,-0.208 13.728,-0.208 17.368,0 25.9,10.816 25.9,24.648 0,18.204 -12.9,23.604 -24.44,23.604 -8.32,0 -11.956,-0.208 -15.18,-0.208 -3.124,0 -11.028,0.208 -11.028,0.208 l -0.104,-0.208 v -2.184 c -0.008,-0.3 0.204,-0.612 0.616,-0.612 z m 23.196,0 c 11.436,0 17.892,-4.26 17.892,-18.828 0,-10.812 -2.396,-23.404 -19.868,-23.404 -4.056,0 -6.656,0.624 -6.656,3.232 v 34.32 c 0,2.904 0.724,4.68 8.632,4.68 z"
|
|
47
|
+
/>
|
|
48
|
+
<path
|
|
49
|
+
fill="currentColor"
|
|
50
|
+
d="m 761.60736,854.23088 c 0,2.288 1.04,3.64 4.368,3.952 l 2.184,0.212 c 0.312,0 0.624,0.204 0.624,0.62 v 2.184 l -0.104,0.212 c 0,0 -7.904,-0.212 -11.124,-0.212 -2.916,0 -11.024,0.212 -11.024,0.212 l -0.212,-0.212 v -2.184 c 0,-0.416 0.312,-0.62 0.728,-0.62 l 2.084,-0.212 c 3.428,-0.312 4.472,-1.976 4.472,-3.952 v -34.632 c 0,-2.284 -1.252,-2.916 -4.472,-3.22 l -2.084,-0.216 c -0.416,0 -0.728,-0.096 -0.728,-0.416 v -2.396 l 0.104,-0.208 c 0,0 8.216,0.208 11.432,0.208 2.808,0 10.82,-0.208 10.82,-0.208 l 0.1,0.208 v 2.288 c 0,0.312 -0.212,0.52 -0.624,0.52 l -2.184,0.212 c -3.224,0.308 -4.368,1.144 -4.368,3.228 l 0.008,34.632 0,0 z"
|
|
51
|
+
/>
|
|
52
|
+
<path
|
|
53
|
+
fill="currentColor"
|
|
54
|
+
d="m 793.64736,858.18288 3.016,0.212 c 0.416,0 0.832,0.312 0.832,0.828 v 1.976 l -0.208,0.212 c 0,0 -11.336,-0.212 -13.832,-0.212 -2.708,0 -9.364,0.212 -9.364,0.212 l -0.212,-0.212 v -1.976 c 0,-0.516 0.312,-0.828 0.836,-0.828 4.58,-0.312 7.172,-1.148 10.084,-7.804 l 23.084,-54.084 c 1.14,-2.704 1.976,-3.748 3.54,-3.748 1.452,0 1.976,1.044 2.804,3.124 l 21.32,56.368 c 1.46,4.06 3.54,5.616 7.7,6.032 l 1.144,0.1 c 0.52,0 1.04,0.312 1.04,0.836 v 1.972 l -0.312,0.208 c 0,0 -8.008,-0.208 -12.064,-0.208 -3.64,0 -13.836,0.208 -13.836,0.208 l -0.1,-0.208 v -1.972 c 0,-0.524 0.208,-0.836 0.736,-0.836 l 2.912,-0.208 c 3.948,-0.308 5.412,-1.564 4.368,-4.472 l -6.344,-17.58 h -21.632 c -2.496,0 -3.12,0.416 -3.636,1.764 l -6.036,15.084 c -1.04,2.608 0.312,4.9 4.16,5.212 z m 25.48,-26.424 -10.088,-27.764 h -0.208 l -10.4,26.416 c -0.416,1.044 0.308,1.352 2.596,1.352 h 18.1 z"
|
|
55
|
+
/>
|
|
56
|
+
</g>
|
|
57
|
+
</svg>
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const fetcher = (...args: Parameters<typeof fetch>) =>
|
|
61
|
+
fetch(...args).then((res) => {
|
|
62
|
+
if (res.status === 200) return res.json();
|
|
63
|
+
throw new Error(`Content returned with status ${res.status}.`);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const ENGLISH_WIKIPEDIA = 'https://en.wikipedia.org/';
|
|
67
|
+
function createWikiUrl(name: string, wiki?: string): string {
|
|
68
|
+
return `${wiki || ENGLISH_WIKIPEDIA}wiki/${name}`;
|
|
69
|
+
}
|
|
70
|
+
function createWikiApiUrl(name: string, wiki?: string): string {
|
|
71
|
+
return `${wiki || ENGLISH_WIKIPEDIA}api/rest_v1/page/summary/${name}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function WikiChild({ page, wiki, open }: { page: string; wiki: string; open: boolean }) {
|
|
75
|
+
const { data, error } = useSWR(open ? createWikiApiUrl(page, wiki) : null, fetcher);
|
|
76
|
+
const { thumbnail, extract, content_urls } = data ?? {};
|
|
77
|
+
const url = content_urls?.desktop?.page ?? createWikiUrl(page);
|
|
78
|
+
const image = thumbnail?.source;
|
|
79
|
+
if (error) {
|
|
80
|
+
return (
|
|
81
|
+
<span>
|
|
82
|
+
<a href={url} className="block" target="_blank" rel="noreferrer">
|
|
83
|
+
<ExternalLinkIcon className="w-4 h-4 float-right" />
|
|
84
|
+
<WikiTextMark />
|
|
85
|
+
</a>
|
|
86
|
+
<div className="mt-2">Error loading "{page}" from wikipedia.</div>
|
|
87
|
+
</span>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return (
|
|
91
|
+
<LinkCard
|
|
92
|
+
loading={!data}
|
|
93
|
+
url={url}
|
|
94
|
+
title={<WikiTextMark />}
|
|
95
|
+
thumbnail={image}
|
|
96
|
+
description={extract}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function WikiLink({
|
|
102
|
+
children,
|
|
103
|
+
page,
|
|
104
|
+
url,
|
|
105
|
+
wiki,
|
|
106
|
+
}: {
|
|
107
|
+
children: React.ReactNode;
|
|
108
|
+
page: string;
|
|
109
|
+
url: string;
|
|
110
|
+
wiki: string;
|
|
111
|
+
}) {
|
|
112
|
+
return (
|
|
113
|
+
<HoverPopover card={({ open }) => <WikiChild wiki={wiki} page={page} open={open} />}>
|
|
114
|
+
<a href={url} className="italic" target="_blank" rel="noreferrer">
|
|
115
|
+
{children}
|
|
116
|
+
</a>
|
|
117
|
+
</HoverPopover>
|
|
118
|
+
);
|
|
119
|
+
}
|
package/src/math.tsx
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import classNames from 'classnames';
|
|
2
|
+
import { ExclamationIcon } from '@heroicons/react/outline';
|
|
3
|
+
import type { InlineMath, Math } from 'myst-spec';
|
|
4
|
+
import { InlineError } from './inlineError';
|
|
5
|
+
import { HashLink } from './heading';
|
|
6
|
+
import type { NodeRenderer } from './types';
|
|
7
|
+
|
|
8
|
+
// function Math({ value, html }: { value: string; html: string }) {
|
|
9
|
+
// const [loaded, setLoaded] = useState(false);
|
|
10
|
+
// const ref = useRef<HTMLDivElement | null>(null);
|
|
11
|
+
// useEffect(() => {
|
|
12
|
+
// import('katex').then(() => {
|
|
13
|
+
// setLoaded(true);
|
|
14
|
+
// });
|
|
15
|
+
// }, []);
|
|
16
|
+
// useEffect(() => {
|
|
17
|
+
// if (!loaded) return;
|
|
18
|
+
// import('katex').then(({ default: katex }) => {
|
|
19
|
+
// if (!ref.current) return;
|
|
20
|
+
// katex.render(value, ref.current, { displayMode: true });
|
|
21
|
+
// });
|
|
22
|
+
// }, [loaded, ref]);
|
|
23
|
+
// return (
|
|
24
|
+
// <>
|
|
25
|
+
// {(typeof document === 'undefined' || !loaded) && (
|
|
26
|
+
// <div dangerouslySetInnerHTML={{ __html: html }} />
|
|
27
|
+
// )}
|
|
28
|
+
// {loaded && <div ref={ref} />}
|
|
29
|
+
// </>
|
|
30
|
+
// );
|
|
31
|
+
// }
|
|
32
|
+
|
|
33
|
+
type MathLike = (InlineMath | Math) & {
|
|
34
|
+
error?: boolean;
|
|
35
|
+
message?: string;
|
|
36
|
+
html?: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const mathRenderer: NodeRenderer<MathLike> = (node) => {
|
|
40
|
+
if (node.type === 'math') {
|
|
41
|
+
if (node.error || !node.html) {
|
|
42
|
+
return (
|
|
43
|
+
<pre key={node.key} title={node.message}>
|
|
44
|
+
<span className="text-red-500">
|
|
45
|
+
<ExclamationIcon className="inline h-[1em] mr-1" />
|
|
46
|
+
{node.message}
|
|
47
|
+
{'\n\n'}
|
|
48
|
+
</span>
|
|
49
|
+
{node.value}
|
|
50
|
+
</pre>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
const id = node.html_id || node.identifier || node.key;
|
|
54
|
+
return (
|
|
55
|
+
<div key={node.key} id={id} className={classNames('flex group')}>
|
|
56
|
+
<div
|
|
57
|
+
dangerouslySetInnerHTML={{ __html: node.html }}
|
|
58
|
+
className="overflow-x-auto flex-grow"
|
|
59
|
+
/>
|
|
60
|
+
{node.enumerator && (
|
|
61
|
+
<div className="flex-none text-right m-0 pl-2 self-center relative">
|
|
62
|
+
<span>({node.enumerator})</span>
|
|
63
|
+
<HashLink id={id} align="right" kind="Equation" />
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
if (node.error || !node.html) {
|
|
70
|
+
return <InlineError key={node.key} value={node.value} message={node.message} />;
|
|
71
|
+
}
|
|
72
|
+
return <span key={node.key} dangerouslySetInnerHTML={{ __html: node.html }} />;
|
|
73
|
+
// return <Math key={node.key} html={node.html} value={node.value as string} />;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const MATH_RENDERERS = {
|
|
77
|
+
math: mathRenderer,
|
|
78
|
+
inlineMath: mathRenderer,
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export default MATH_RENDERERS;
|
package/src/mermaid.tsx
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { NodeRenderer } from './types';
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
async function parse(id: string, text: string): Promise<string> {
|
|
5
|
+
const { default: mermaid } = await import('mermaid');
|
|
6
|
+
return await new Promise<string>((resolve) => {
|
|
7
|
+
mermaid.render(id, text, (code) => {
|
|
8
|
+
resolve(code);
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function MermaidRenderer({ id, value }: { value: string; id: string }) {
|
|
14
|
+
const [graph, setGraph] = useState<string>();
|
|
15
|
+
const [error, setError] = useState<Error>();
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
parse(id, value)
|
|
18
|
+
.then((svg) => {
|
|
19
|
+
setGraph(svg);
|
|
20
|
+
setError(undefined);
|
|
21
|
+
})
|
|
22
|
+
.catch((err) => {
|
|
23
|
+
setGraph(undefined);
|
|
24
|
+
setError(err as Error);
|
|
25
|
+
});
|
|
26
|
+
}, []);
|
|
27
|
+
return (
|
|
28
|
+
<figure className="">
|
|
29
|
+
{graph && <div dangerouslySetInnerHTML={{ __html: graph }}></div>}
|
|
30
|
+
{error && (
|
|
31
|
+
<pre>
|
|
32
|
+
Error parsing mermaid graph.
|
|
33
|
+
{'\n\n'}
|
|
34
|
+
{value}
|
|
35
|
+
</pre>
|
|
36
|
+
)}
|
|
37
|
+
</figure>
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const MermaidNodeRenderer: NodeRenderer = (node) => {
|
|
42
|
+
return <MermaidRenderer key={node.key} id={node.key} value={node.value} />;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const MERMAID_RENDERERS = {
|
|
46
|
+
mermaid: MermaidNodeRenderer,
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export default MERMAID_RENDERERS;
|