@curvenote/renderers 1.0.1 → 2.0.0

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.
Files changed (105) hide show
  1. package/dist/articles.d.ts.map +1 -1
  2. package/dist/articles.js +8 -2
  3. package/dist/components/admonition.d.ts.map +1 -1
  4. package/dist/components/admonition.js +24 -4
  5. package/dist/components/anywidget-host.d.ts +7 -0
  6. package/dist/components/anywidget-host.d.ts.map +1 -0
  7. package/dist/components/anywidget-host.js +11 -0
  8. package/dist/components/bluesky.d.ts +6 -0
  9. package/dist/components/bluesky.d.ts.map +1 -0
  10. package/dist/components/bluesky.js +113 -0
  11. package/dist/components/cite-figurebar.d.ts +2 -1
  12. package/dist/components/cite-figurebar.d.ts.map +1 -1
  13. package/dist/components/cite-figurebar.js +73 -27
  14. package/dist/components/cite.d.ts +11 -0
  15. package/dist/components/cite.d.ts.map +1 -1
  16. package/dist/components/cite.js +130 -8
  17. package/dist/components/copy-icon.d.ts +11 -0
  18. package/dist/components/copy-icon.d.ts.map +1 -0
  19. package/dist/components/copy-icon.js +32 -0
  20. package/dist/components/definition-list.d.ts.map +1 -1
  21. package/dist/components/definition-list.js +3 -2
  22. package/dist/components/faq.js +1 -1
  23. package/dist/components/hash-link.d.ts +3 -0
  24. package/dist/components/hash-link.d.ts.map +1 -0
  25. package/dist/components/hash-link.js +3 -0
  26. package/dist/components/hero.d.ts +1 -1
  27. package/dist/components/hero.d.ts.map +1 -1
  28. package/dist/components/hero.js +14 -2
  29. package/dist/components/hover-popover.d.ts +26 -0
  30. package/dist/components/hover-popover.d.ts.map +1 -0
  31. package/dist/components/hover-popover.js +26 -0
  32. package/dist/components/images.d.ts.map +1 -1
  33. package/dist/components/images.js +7 -2
  34. package/dist/components/index.d.ts +3 -0
  35. package/dist/components/index.d.ts.map +1 -1
  36. package/dist/components/index.js +3 -0
  37. package/dist/components/pdb.d.ts.map +1 -1
  38. package/dist/components/pdb.js +42 -6
  39. package/dist/index.d.ts +1 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +11 -3
  42. package/dist/myst/abbreviation.d.ts +6 -0
  43. package/dist/myst/abbreviation.d.ts.map +1 -0
  44. package/dist/myst/abbreviation.js +13 -0
  45. package/dist/myst/anywidget.d.ts +5 -0
  46. package/dist/myst/anywidget.d.ts.map +1 -0
  47. package/dist/myst/anywidget.js +12 -0
  48. package/dist/myst/code.d.ts +6 -0
  49. package/dist/myst/code.d.ts.map +1 -0
  50. package/dist/myst/code.js +103 -0
  51. package/dist/myst/container.d.ts +7 -0
  52. package/dist/myst/container.d.ts.map +1 -0
  53. package/dist/myst/container.js +57 -0
  54. package/dist/myst/cross-reference.d.ts +23 -0
  55. package/dist/myst/cross-reference.d.ts.map +1 -0
  56. package/dist/myst/cross-reference.js +140 -0
  57. package/dist/myst/footnotes.d.ts +7 -0
  58. package/dist/myst/footnotes.d.ts.map +1 -0
  59. package/dist/myst/footnotes.js +32 -0
  60. package/dist/myst/index.d.ts +14 -0
  61. package/dist/myst/index.d.ts.map +1 -0
  62. package/dist/myst/index.js +17 -0
  63. package/dist/myst/inline-expression.d.ts +6 -0
  64. package/dist/myst/inline-expression.d.ts.map +1 -0
  65. package/dist/myst/inline-expression.js +20 -0
  66. package/dist/myst/links/figshare.d.ts +8 -0
  67. package/dist/myst/links/figshare.d.ts.map +1 -0
  68. package/dist/myst/links/figshare.js +45 -0
  69. package/dist/myst/links/geo.d.ts +8 -0
  70. package/dist/myst/links/geo.d.ts.map +1 -0
  71. package/dist/myst/links/geo.js +116 -0
  72. package/dist/myst/links/github.d.ts +15 -0
  73. package/dist/myst/links/github.d.ts.map +1 -0
  74. package/dist/myst/links/github.js +156 -0
  75. package/dist/myst/links/huggingface.d.ts +10 -0
  76. package/dist/myst/links/huggingface.d.ts.map +1 -0
  77. package/dist/myst/links/huggingface.js +73 -0
  78. package/dist/myst/links/index.d.ts +12 -0
  79. package/dist/myst/links/index.d.ts.map +1 -0
  80. package/dist/myst/links/index.js +95 -0
  81. package/dist/myst/links/ror.d.ts +7 -0
  82. package/dist/myst/links/ror.d.ts.map +1 -0
  83. package/dist/myst/links/ror.js +34 -0
  84. package/dist/myst/links/rrid.d.ts +5 -0
  85. package/dist/myst/links/rrid.d.ts.map +1 -0
  86. package/dist/myst/links/rrid.js +31 -0
  87. package/dist/myst/links/wiki.d.ts +9 -0
  88. package/dist/myst/links/wiki.d.ts.map +1 -0
  89. package/dist/myst/links/wiki.js +41 -0
  90. package/dist/myst/math.d.ts +15 -0
  91. package/dist/myst/math.d.ts.map +1 -0
  92. package/dist/myst/math.js +53 -0
  93. package/dist/transforms/articles.d.ts +2 -1
  94. package/dist/transforms/articles.d.ts.map +1 -1
  95. package/dist/transforms/articles.js +2 -2
  96. package/dist/transforms/index.d.ts +5 -2
  97. package/dist/transforms/index.d.ts.map +1 -1
  98. package/dist/transforms/index.js +1 -0
  99. package/dist/utils/anywidget-analytics.d.ts +13 -0
  100. package/dist/utils/anywidget-analytics.d.ts.map +1 -0
  101. package/dist/utils/anywidget-analytics.js +15 -0
  102. package/dist/utils/content-analytics.d.ts +44 -0
  103. package/dist/utils/content-analytics.d.ts.map +1 -0
  104. package/dist/utils/content-analytics.js +59 -0
  105. package/package.json +22 -13
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from '@curvenote/react-utils';
3
+ import { useAnalytics, useDesign } from '@curvenote/theme-ui';
4
+ import { useXRefState } from '@myst-theme/providers';
5
+ import { MyST } from 'myst-to-react';
6
+ import { useCallback, useId, useLayoutEffect, useRef, useState } from 'react';
7
+ const container = ({ node, className }) => {
8
+ const figureName = `fig-${node.kind}`;
9
+ const { inCrossRef } = useXRefState();
10
+ const { breakout_containers } = useDesign();
11
+ return (_jsx("figure", { id: node.html_id || node.identifier || node.key, className: cn({
12
+ 'md:-mx-4 lg:-mx-6 xl:-mx-10': breakout_containers && !node.subcontainer && !inCrossRef,
13
+ }, { [figureName]: !!node.kind, subcontainer: node.subcontainer }, node.class, className), children: _jsx(MyST, { ast: node.children }) }));
14
+ };
15
+ function CaptionContent({ node }) {
16
+ const capture = useAnalytics();
17
+ const [expanded, setExpanded] = useState(false);
18
+ const [isTruncatable, setIsTruncatable] = useState(null);
19
+ const contentRef = useRef(null);
20
+ const contentId = useId();
21
+ const trackCaptionToggle = useCallback((nextExpanded) => {
22
+ capture(nextExpanded ? 'content_caption_expanded' : 'content_caption_collapsed', {
23
+ surface: 'content',
24
+ captionIdentifier: node.identifier,
25
+ captionLabel: node.label,
26
+ });
27
+ }, [capture, node.identifier, node.label]);
28
+ useLayoutEffect(() => {
29
+ const element = contentRef.current;
30
+ if (!element || expanded)
31
+ return;
32
+ const measure = () => {
33
+ setIsTruncatable(element.scrollHeight > element.clientHeight + 1);
34
+ };
35
+ measure();
36
+ const observer = new ResizeObserver(measure);
37
+ observer.observe(element);
38
+ return () => observer.disconnect();
39
+ }, [node, expanded]);
40
+ const shouldClamp = !expanded && isTruncatable !== false;
41
+ return (_jsxs(_Fragment, { children: [_jsx("div", { ref: contentRef, id: contentId, className: cn('*:first:mt-0', shouldClamp && 'line-clamp-4'), children: _jsx(MyST, { ast: node.children }) }), isTruncatable && (_jsx("div", { className: "flex justify-end", children: _jsx("button", { type: "button", "aria-expanded": expanded, "aria-controls": contentId, onClick: () => {
42
+ const nextExpanded = !expanded;
43
+ setExpanded(nextExpanded);
44
+ trackCaptionToggle(nextExpanded);
45
+ }, className: "mt-0.5 text-xs text-blue-600 underline-offset-2 cursor-pointer hover:text-blue-700 hover:underline dark:text-blue-400 dark:hover:text-blue-300", children: expanded ? 'Show less' : 'Show more' }) }))] }));
46
+ }
47
+ const caption = ({ node, className }) => {
48
+ return (_jsx("figcaption", { className: cn('group', className), children: _jsx(CaptionContent, { node: node }) }));
49
+ };
50
+ const legend = ({ node, className }) => {
51
+ return (_jsx("figcaption", { className: cn('text-sm', className), children: _jsx(MyST, { ast: node.children }) }));
52
+ };
53
+ export const CONTAINER_RENDERERS = {
54
+ container,
55
+ caption,
56
+ legend,
57
+ };
@@ -0,0 +1,23 @@
1
+ import { type ReactNode } from 'react';
2
+ import { type NodeRenderer } from '@myst-theme/providers';
3
+ export declare function useFetchMdast({ remote, url, remoteBaseUrl, dataUrl, }: {
4
+ remote?: boolean;
5
+ url?: string;
6
+ remoteBaseUrl?: string;
7
+ dataUrl?: string;
8
+ }): import("swr").SWRResponse<any, any, any>;
9
+ export declare function CrossReferenceHover({ url: urlIn, dataUrl: dataUrlIn, remote: remoteIn, remoteBaseUrl: remoteBaseUrlIn, children, identifier, className, htmlId, }: {
10
+ url?: string;
11
+ dataUrl?: string;
12
+ remote?: boolean;
13
+ remoteBaseUrl?: string;
14
+ children: ReactNode;
15
+ identifier?: string;
16
+ className?: string;
17
+ htmlId?: string;
18
+ }): import("react/jsx-runtime").JSX.Element;
19
+ export declare const CrossReferenceNode: NodeRenderer;
20
+ export declare const CROSS_REFERENCE_RENDERERS: {
21
+ crossReference: NodeRenderer;
22
+ };
23
+ //# sourceMappingURL=cross-reference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cross-reference.d.ts","sourceRoot":"","sources":["../../src/myst/cross-reference.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAmB,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGxD,OAAO,EAQL,KAAK,YAAY,EAClB,MAAM,uBAAuB,CAAC;AA8D/B,wBAAgB,aAAa,CAAC,EAC5B,MAAM,EACN,GAAG,EACH,aAAa,EACb,OAAO,GACR,EAAE;IACD,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,4CAIA;AA+BD,wBAAgB,mBAAmB,CAAC,EAClC,GAAG,EAAE,KAAK,EACV,OAAO,EAAE,SAAS,EAClB,MAAM,EAAE,QAAQ,EAChB,aAAa,EAAE,eAAe,EAC9B,QAAQ,EACR,UAAU,EACV,SAAS,EACT,MAAW,GACZ,EAAE;IACD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,2CA0FA;AAED,eAAO,MAAM,kBAAkB,EAAE,YA0BhC,CAAC;AAEF,eAAO,MAAM,yBAAyB;;CAErC,CAAC"}
@@ -0,0 +1,140 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // @curvenote-fork:myst-to-react
3
+ // MIT — https://github.com/jupyter-book/myst-theme (packages/myst-to-react)
4
+ // Baseline v1.1.4: https://github.com/jupyter-book/myst-theme/blob/e2e1db449c87294775717a8924d4dd1a4d32ceb2/packages/myst-to-react/src/crossReference.tsx
5
+ // Patched v1.3.0: https://github.com/jupyter-book/myst-theme/blob/be74efbb7d13b926a2a6c1e67913e1b1029f28e6/packages/myst-to-react/src/crossReference.tsx
6
+ import {} from 'react';
7
+ import { useAnalytics } from '@curvenote/theme-ui';
8
+ import { MYST_SPEC_VERSION } from '@myst-theme/common';
9
+ import { useLinkProvider, useReferences, useBaseurl, withBaseurl, XRefProvider, useXRefState, useFrontmatter, } from '@myst-theme/providers';
10
+ import { selectMdastNodes } from 'myst-common';
11
+ import { migrate } from 'myst-migrate';
12
+ import useSWR from 'swr';
13
+ import { scrollToElement } from '../components/hash-link';
14
+ import { MyST } from 'myst-to-react';
15
+ import { cn } from '@curvenote/react-utils';
16
+ import { InlineError } from '../components/inlineError';
17
+ import { HoverPopover } from '../components/hover-popover';
18
+ const fetcher = (...args) => fetch(...args).then(async (res) => {
19
+ if (res.status === 200) {
20
+ let data = await res.json();
21
+ try {
22
+ data = await migrate({ version: data.version ?? 0, ...data }, { to: MYST_SPEC_VERSION });
23
+ }
24
+ catch (error) {
25
+ console.error(`Error migrating content for ${args[0]} (aborted):`, error);
26
+ }
27
+ return data;
28
+ }
29
+ throw new Error(`Content returned with status ${res.status}.`);
30
+ });
31
+ function XrefChildren({ load, identifier }) {
32
+ const data = useSelectNodes({ load, identifier });
33
+ if (!data)
34
+ return null;
35
+ if (data.loading)
36
+ return _jsx(_Fragment, { children: "Loading..." });
37
+ if (data.error)
38
+ return _jsx(_Fragment, { children: "Error loading remote page." });
39
+ if (!data.nodes || data.nodes.length === 0) {
40
+ return _jsx(InlineError, { value: identifier || 'No Label', message: "Cross Reference Not Found" });
41
+ }
42
+ return _jsx(MyST, { ast: data.nodes });
43
+ }
44
+ function createRemoteBaseUrl(url, remoteBaseUrl) {
45
+ if (remoteBaseUrl && url?.startsWith(remoteBaseUrl))
46
+ return url;
47
+ return `${remoteBaseUrl || ''}${url || ''}`;
48
+ }
49
+ function createExternalUrl({ url, remoteBaseUrl, dataUrl, baseurl, }) {
50
+ if (remoteBaseUrl || dataUrl?.startsWith('http')) {
51
+ if (!dataUrl) {
52
+ console.error('Expected external URL to provide a dataUrl');
53
+ return null;
54
+ }
55
+ return createRemoteBaseUrl(dataUrl, remoteBaseUrl);
56
+ }
57
+ if (dataUrl)
58
+ return withBaseurl(dataUrl, baseurl);
59
+ return `${withBaseurl(url, baseurl)}.json`;
60
+ }
61
+ export function useFetchMdast({ remote, url, remoteBaseUrl, dataUrl, }) {
62
+ const baseurl = useBaseurl();
63
+ const lookupUrl = createExternalUrl({ url, remoteBaseUrl, dataUrl, baseurl });
64
+ return useSWR(remote ? lookupUrl : null, fetcher);
65
+ }
66
+ function useSelectNodes({ load, identifier }) {
67
+ const references = useReferences();
68
+ const frontmatter = useFrontmatter();
69
+ const { remote, url, remoteBaseUrl, dataUrl } = useXRefState();
70
+ const { data, error } = useFetchMdast({
71
+ remote: remote && load,
72
+ url,
73
+ remoteBaseUrl,
74
+ dataUrl,
75
+ });
76
+ if (!load)
77
+ return undefined;
78
+ const mdast = data ? data.mdast : references?.article;
79
+ const parts = data ? data.frontmatter?.parts : frontmatter?.parts;
80
+ let nodes = [];
81
+ let htmlId;
82
+ const trees = [
83
+ { mdast },
84
+ ...Object.values(parts ?? {}),
85
+ ];
86
+ trees.forEach((part) => {
87
+ const tree = part.mdast;
88
+ if (!tree || nodes.length > 0)
89
+ return;
90
+ const selected = selectMdastNodes(tree, identifier ?? '', 3);
91
+ nodes = selected.nodes;
92
+ htmlId = selected.htmlId;
93
+ });
94
+ return { htmlId, nodes, loading: remote && !data, error: remote && error };
95
+ }
96
+ export function CrossReferenceHover({ url: urlIn, dataUrl: dataUrlIn, remote: remoteIn, remoteBaseUrl: remoteBaseUrlIn, children, identifier, className, htmlId = '', }) {
97
+ const Link = useLinkProvider();
98
+ const baseurl = useBaseurl();
99
+ const parent = useXRefState();
100
+ const remoteBaseUrl = remoteBaseUrlIn ?? parent.remoteBaseUrl;
101
+ const remote = !!remoteBaseUrl || parent.remote || remoteIn;
102
+ const url = parent.remote ? (urlIn ?? parent.url) : urlIn;
103
+ const dataUrl = parent.remote ? (dataUrlIn ?? parent.dataUrl) : dataUrlIn;
104
+ const external = !!remoteBaseUrl || (url?.startsWith('http') ?? false);
105
+ const capture = useAnalytics();
106
+ const scroll = (e) => {
107
+ e.preventDefault();
108
+ if (!htmlId)
109
+ return;
110
+ capture('content_hash_link_clicked', {
111
+ surface: 'content',
112
+ linkKind: 'cross_reference',
113
+ targetId: htmlId,
114
+ crossReferenceIdentifier: identifier,
115
+ crossReferenceRemote: remote,
116
+ });
117
+ const el = document.getElementById(htmlId);
118
+ scrollToElement(el, { htmlId });
119
+ };
120
+ const isButtonLike = (className ?? '').split(' ').includes('button');
121
+ return (_jsx(HoverPopover, { card: ({ load }) => (_jsx(XRefProvider, { remote: remote, remoteBaseUrl: remoteBaseUrl, url: url, dataUrl: dataUrl, children: _jsxs("div", { className: "hover-document article w-[500px] sm:max-w-[500px] overflow-auto", children: [remoteBaseUrl && (_jsxs("div", { className: "px-3 py-1 w-full text-xs bg-gray-50 border-b", children: [_jsx("strong", { className: "text-gray-700", children: "Source: " }), _jsx("a", { className: cn('text-gray-700', className), href: `${createRemoteBaseUrl(url, remoteBaseUrl)}${htmlId ? `#${htmlId}` : ''}`, target: "_blank", rel: "noreferrer", children: remoteBaseUrl })] })), _jsx("div", { className: "px-3", children: _jsx(XrefChildren, { load: load, identifier: identifier }) })] }) })), analytics: {
122
+ cardKind: 'cross_reference',
123
+ properties: {
124
+ crossReferenceIdentifier: identifier,
125
+ crossReferenceRemote: remote,
126
+ crossReferenceUrl: url,
127
+ crossReferenceHtmlId: htmlId,
128
+ },
129
+ }, children: _jsxs("span", { children: [remote && external && (_jsx("a", { href: `${createRemoteBaseUrl(url, remoteBaseUrl)}${htmlId ? `#${htmlId}` : ''}`, target: "_blank", rel: "noreferrer", className: cn({ 'hover-link': !isButtonLike }, className), children: children })), remote && !external && (_jsx(Link, { to: `${withBaseurl(url, baseurl)}${htmlId ? `#${htmlId}` : ''}`, prefetch: "intent", className: cn({ 'hover-link': !isButtonLike }, className), children: children })), !remote && (_jsx("a", { href: `#${htmlId}`, onClick: scroll, className: cn({ 'hover-link': !isButtonLike }, className), children: children }))] }) }));
130
+ }
131
+ export const CrossReferenceNode = ({ node, className }) => {
132
+ if (!node.children) {
133
+ return (_jsx(InlineError, { value: node.label || node.identifier || 'No Label', message: "Cross Reference Not Found", className: className }));
134
+ }
135
+ const { remote, url, dataUrl, remoteBaseUrl, identifier, html_id, class: nodeClass } = node;
136
+ return (_jsxs(CrossReferenceHover, { identifier: identifier, htmlId: html_id, remote: remote, url: url, dataUrl: dataUrl, remoteBaseUrl: remoteBaseUrl, className: cn(nodeClass, className), children: [node.prefix && _jsx(_Fragment, { children: node.prefix }), _jsx(MyST, { ast: node.children }), node.suffix || null] }));
137
+ };
138
+ export const CROSS_REFERENCE_RENDERERS = {
139
+ crossReference: CrossReferenceNode,
140
+ };
@@ -0,0 +1,7 @@
1
+ import { type NodeRenderer } from '@myst-theme/providers';
2
+ export declare const FootnoteReference: NodeRenderer;
3
+ export declare const FOOTNOTE_RENDERERS: {
4
+ footnoteReference: NodeRenderer;
5
+ footnoteDefinition: () => null;
6
+ };
7
+ //# sourceMappingURL=footnotes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"footnotes.d.ts","sourceRoot":"","sources":["../../src/myst/footnotes.tsx"],"names":[],"mappings":"AAKA,OAAO,EAA+B,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAuBvF,eAAO,MAAM,iBAAiB,EAAE,YA6B/B,CAAC;AAEF,eAAO,MAAM,kBAAkB;;;CAG9B,CAAC"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // @curvenote-fork:myst-to-react
3
+ // MIT — https://github.com/jupyter-book/myst-theme (packages/myst-to-react)
4
+ // Baseline v1.1.4: https://github.com/jupyter-book/myst-theme/blob/e2e1db449c87294775717a8924d4dd1a4d32ceb2/packages/myst-to-react/src/footnotes.tsx
5
+ // Patched v1.3.0: https://github.com/jupyter-book/myst-theme/blob/be74efbb7d13b926a2a6c1e67913e1b1029f28e6/packages/myst-to-react/src/footnotes.tsx
6
+ import { XRefProvider, useReferences } from '@myst-theme/providers';
7
+ import { select } from 'unist-util-select';
8
+ import { MyST } from 'myst-to-react';
9
+ import { HashLink } from '../components/hash-link';
10
+ import { HoverPopover } from '../components/hover-popover';
11
+ function FootnoteDefinition({ identifier }) {
12
+ const references = useReferences();
13
+ const node = references
14
+ ?.footnotes?.[identifier] ??
15
+ select(`footnoteDefinition[identifier=${identifier}]`, references?.article);
16
+ const footnoteNode = node;
17
+ return (_jsx(XRefProvider, { children: _jsx("div", { className: "hover-document article w-[500px] sm:max-w-[500px] px-3 text-sm", children: _jsx(MyST, { ast: footnoteNode?.children }) }) }));
18
+ }
19
+ export const FootnoteReference = ({ node, className }) => {
20
+ const label = node.enumerator ?? node.number ?? node.identifier;
21
+ return (_jsx(HoverPopover, { openDelay: 0, card: _jsx(FootnoteDefinition, { identifier: node.identifier }), analytics: {
22
+ cardKind: 'footnote',
23
+ properties: { footnoteIdentifier: node.identifier, footnoteLabel: label },
24
+ }, children: _jsx("span", { id: `fnref-${node.key}`, className: className, children: _jsx("sup", { className: "hover-link", children: _jsxs(HashLink, { id: `fn-${node.identifier}`, title: "Link to Footnote", scrollBehavior: "instant", canSelectText: true, analytics: {
25
+ linkKind: 'footnote_reference',
26
+ properties: { footnoteIdentifier: node.identifier },
27
+ }, children: ["[", label, "]"] }) }) }) }));
28
+ };
29
+ export const FOOTNOTE_RENDERERS = {
30
+ footnoteReference: FootnoteReference,
31
+ footnoteDefinition: () => null,
32
+ };
@@ -0,0 +1,14 @@
1
+ export declare const MYST_RENDERERS: {
2
+ container: import("@myst-theme/providers").NodeRenderer;
3
+ caption: import("@myst-theme/providers").NodeRenderer;
4
+ legend: import("@myst-theme/providers").NodeRenderer;
5
+ inlineExpression: import("@myst-theme/providers").NodeRenderer;
6
+ abbreviation: import("@myst-theme/providers").NodeRenderer;
7
+ crossReference: import("@myst-theme/providers").NodeRenderer;
8
+ footnoteReference: import("@myst-theme/providers").NodeRenderer;
9
+ footnoteDefinition: () => null;
10
+ code: import("@myst-theme/providers").NodeRenderer;
11
+ inlineCode: import("@myst-theme/providers").NodeRenderer;
12
+ };
13
+ export { useFetchMdast } from './cross-reference.js';
14
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/myst/index.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,cAAc;;;;;;;;;;;CAQ1B,CAAC;AAEF,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { CODE_RENDERERS } from './code.js';
2
+ import { FOOTNOTE_RENDERERS } from './footnotes.js';
3
+ import { CROSS_REFERENCE_RENDERERS } from './cross-reference.js';
4
+ import { ABBREVIATION_RENDERERS } from './abbreviation.js';
5
+ import { INLINE_EXPRESSION_RENDERERS } from './inline-expression.js';
6
+ import { MYST_LINK_RENDERERS } from './links/index.js';
7
+ import { CONTAINER_RENDERERS } from './container.js';
8
+ export const MYST_RENDERERS = {
9
+ ...CODE_RENDERERS,
10
+ ...FOOTNOTE_RENDERERS,
11
+ ...CROSS_REFERENCE_RENDERERS,
12
+ ...ABBREVIATION_RENDERERS,
13
+ ...INLINE_EXPRESSION_RENDERERS,
14
+ ...MYST_LINK_RENDERERS,
15
+ ...CONTAINER_RENDERERS,
16
+ };
17
+ export { useFetchMdast } from './cross-reference.js';
@@ -0,0 +1,6 @@
1
+ import type { NodeRenderer } from '@myst-theme/providers';
2
+ export declare const InlineExpression: NodeRenderer;
3
+ export declare const INLINE_EXPRESSION_RENDERERS: {
4
+ inlineExpression: NodeRenderer;
5
+ };
6
+ //# sourceMappingURL=inline-expression.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inline-expression.d.ts","sourceRoot":"","sources":["../../src/myst/inline-expression.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAM1D,eAAO,MAAM,gBAAgB,EAAE,YA2B9B,CAAC;AAEF,eAAO,MAAM,2BAA2B;;CAEvC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { MyST } from 'myst-to-react';
3
+ import { cn } from '@curvenote/react-utils';
4
+ import { InlineError } from '../components/inlineError';
5
+ import { Tooltip } from '../components/hover-popover';
6
+ export const InlineExpression = ({ node, className }) => {
7
+ if (!node.result) {
8
+ return (_jsx(InlineError, { value: `Unexecuted inline expression for: ${node.value}`, className: className }));
9
+ }
10
+ if (node.result.status !== 'ok') {
11
+ return (_jsx(InlineError, { value: `${node.result.ename}: ${node.result.evalue}`, className: className }));
12
+ }
13
+ return (_jsx(Tooltip, { title: _jsx("code", { children: node.value }), analytics: {
14
+ cardKind: 'inline_expression',
15
+ properties: { expression: node.value },
16
+ }, children: _jsx("span", { className: cn('border-b border-dotted cursor-help', className), children: _jsx(MyST, { ast: node.children }) }) }));
17
+ };
18
+ export const INLINE_EXPRESSION_RENDERERS = {
19
+ inlineExpression: InlineExpression,
20
+ };
@@ -0,0 +1,8 @@
1
+ import type { ReactNode } from 'react';
2
+ export declare function FigshareLink({ id, url, children, className, }: {
3
+ id: string;
4
+ url: string;
5
+ children?: ReactNode;
6
+ className?: string;
7
+ }): import("react/jsx-runtime").JSX.Element;
8
+ //# sourceMappingURL=figshare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"figshare.d.ts","sourceRoot":"","sources":["../../../src/myst/links/figshare.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AA2GvC,wBAAgB,YAAY,CAAC,EAC3B,EAAE,EACF,GAAG,EACH,QAAQ,EACR,SAAS,GACV,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,2CAgBA"}
@@ -0,0 +1,45 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import useSWR from 'swr';
3
+ import { cn } from '@curvenote/react-utils';
4
+ import { HoverPopover } from '../../components/hover-popover';
5
+ const fetcher = (...args) => fetch(...args).then((res) => {
6
+ if (res.status === 200)
7
+ return res.json();
8
+ throw new Error(`Content returned with status ${res.status}.`);
9
+ });
10
+ function humanSize(bytes) {
11
+ if (!bytes)
12
+ return '';
13
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
14
+ const i = Math.min(units.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)));
15
+ const value = bytes / 1024 ** i;
16
+ return `${value >= 10 || i === 0 ? Math.round(value) : value.toFixed(1)} ${units[i]}`;
17
+ }
18
+ function authorList(authors) {
19
+ if (authors.length === 0)
20
+ return '';
21
+ if (authors.length <= 3)
22
+ return authors.join(', ');
23
+ return `${authors.slice(0, 3).join(', ')} +${authors.length - 3}`;
24
+ }
25
+ function FigshareChild({ id }) {
26
+ const { data, error } = useSWR(`/figshare/${id}`, fetcher, {
27
+ revalidateOnFocus: false,
28
+ dedupingInterval: 60_000,
29
+ });
30
+ if (!data && !error) {
31
+ return (_jsx("div", { className: "hover-document article w-[500px] sm:max-w-[500px] animate-pulse", children: "Loading..." }));
32
+ }
33
+ if (error || !data) {
34
+ return (_jsxs("div", { className: "hover-document article w-[500px] sm:max-w-[500px]", children: ["Error loading figshare ", id, "."] }));
35
+ }
36
+ const meta = [
37
+ data.fileCount > 0 && `${data.fileCount} file${data.fileCount === 1 ? '' : 's'}`,
38
+ humanSize(data.size),
39
+ data.license,
40
+ ].filter(Boolean);
41
+ return (_jsxs("div", { className: "hover-document article w-[500px] sm:max-w-[500px] p-3", children: [_jsxs("p", { className: "text-sm font-light", children: [_jsx("span", { className: "font-semibold text-[#c4262e]", children: "figshare" }), data.type ? ` · ${data.type}` : '', " ", data.published && _jsxs("span", { children: ["\u00B7 ", data.published] })] }), _jsx("div", { className: "mb-1 text-xl font-bold", children: _jsx("a", { href: data.url, target: "_blank", rel: "noopener noreferrer", className: "text-inherit hover:underline", children: data.title }) }), data.authors.length > 0 && (_jsx("p", { className: "mb-2 text-xs font-light", children: authorList(data.authors) })), data.description && _jsx("p", { className: "text-md line-clamp-3", children: data.description }), meta.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-3 mt-3 text-xs font-light", children: meta.map((m) => (_jsx("span", { children: m }, m))) })), data.tags.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-1 mt-2", children: data.tags.slice(0, 6).map((tag) => (_jsx("span", { className: "text-xs inline-flex items-center px-2 py-0.5 rounded-full border", children: tag }, tag))) }))] }));
42
+ }
43
+ export function FigshareLink({ id, url, children, className, }) {
44
+ return (_jsx(HoverPopover, { card: _jsx(FigshareChild, { id: id }), analytics: { cardKind: 'figshare_link', properties: { id } }, children: _jsx("a", { href: url, target: "_blank", rel: "noopener noreferrer", className: cn('hover-link', className), children: children }) }));
45
+ }
@@ -0,0 +1,8 @@
1
+ export type NcbiDb = 'gds' | 'bioproject';
2
+ export declare function GEOLink({ accession, db, children, className, }: {
3
+ accession: string;
4
+ db?: NcbiDb;
5
+ children?: React.ReactNode;
6
+ className?: string;
7
+ }): import("react/jsx-runtime").JSX.Element;
8
+ //# sourceMappingURL=geo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"geo.d.ts","sourceRoot":"","sources":["../../../src/myst/links/geo.tsx"],"names":[],"mappings":"AAUA,MAAM,MAAM,MAAM,GAAG,KAAK,GAAG,YAAY,CAAC;AAyK1C,wBAAgB,OAAO,CAAC,EACtB,SAAS,EACT,EAAU,EACV,QAAQ,EACR,SAAS,GACV,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,2CAgBA"}
@@ -0,0 +1,116 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import useSWR from 'swr';
3
+ import { cn } from '@curvenote/react-utils';
4
+ import { HoverPopover } from '../../components/hover-popover';
5
+ const EUTILS = 'https://eutils.ncbi.nlm.nih.gov/entrez/eutils';
6
+ // NCBI asks E-utilities callers to identify themselves.
7
+ const TOOL = 'tool=curvenote-reader';
8
+ const GEO_CGI = 'https://www.ncbi.nlm.nih.gov/geo/query/acc.cgi';
9
+ const BIOPROJECT = 'https://www.ncbi.nlm.nih.gov/bioproject';
10
+ // GEO/GDS and PRJNA UIDs are deterministic, so we can skip esearch and hit esummary directly —
11
+ // one request per hover instead of two, which matters under NCBI's 3 req/sec limit.
12
+ const UID_OFFSET = {
13
+ GPL: 100_000_000,
14
+ GSE: 200_000_000,
15
+ GSM: 300_000_000,
16
+ GDS: 0,
17
+ };
18
+ function directUid(accession) {
19
+ const geo = accession.match(/^(GSE|GSM|GPL|GDS)(\d+)$/);
20
+ if (geo)
21
+ return String(UID_OFFSET[geo[1]] + Number(geo[2]));
22
+ // PRJNA accessions encode their UID; PRJEB/PRJDB don't, so those fall back to esearch.
23
+ const prjna = accession.match(/^PRJNA(\d+)$/);
24
+ if (prjna)
25
+ return prjna[1];
26
+ return undefined;
27
+ }
28
+ class RetryableError extends Error {
29
+ }
30
+ // eutils sends `Access-Control-Allow-Origin: *`, so we can fetch client-side. When throttled,
31
+ // NCBI replies with an HTML page rather than JSON; surface that as a retryable error so SWR
32
+ // backs off and retries instead of showing a hard failure.
33
+ async function fetchJson(url) {
34
+ const res = await fetch(url);
35
+ if (res.status === 429)
36
+ throw new RetryableError('Rate limited by NCBI.');
37
+ if (!res.ok)
38
+ throw new Error(`Content returned with status ${res.status}.`);
39
+ if (!(res.headers.get('content-type') ?? '').includes('json')) {
40
+ throw new RetryableError('Non-JSON response from NCBI (likely rate limited).');
41
+ }
42
+ return res.json();
43
+ }
44
+ async function resolveUid(accession, db) {
45
+ const direct = directUid(accession);
46
+ if (direct)
47
+ return direct;
48
+ const search = await fetchJson(`${EUTILS}/esearch.fcgi?db=${db}&retmode=json&${TOOL}&term=${accession}`);
49
+ const uid = search?.esearchresult?.idlist?.[0];
50
+ if (!uid)
51
+ throw new Error(`No ${db} record for ${accession}.`);
52
+ return uid;
53
+ }
54
+ function toGeoCard(r, accession) {
55
+ const acc = r.accession || accession;
56
+ const samples = Number(r.n_samples) > 0 ? `${r.n_samples} samples` : undefined;
57
+ return {
58
+ kindLabel: r.entrytype ? `GEO: ${r.entrytype}` : 'GEO',
59
+ accession: acc,
60
+ title: r.title,
61
+ date: r.pdat,
62
+ badges: [r.taxon, r.gdstype, samples].filter(Boolean),
63
+ summary: r.summary,
64
+ pubmedids: r.pubmedids,
65
+ href: `${GEO_CGI}?acc=${acc}`,
66
+ };
67
+ }
68
+ function toBioProjectCard(r, accession) {
69
+ const acc = r.project_acc || accession;
70
+ return {
71
+ kindLabel: 'BioProject',
72
+ accession: acc,
73
+ title: r.project_title || r.project_name,
74
+ date: r.registration_date?.split(' ')[0],
75
+ badges: [r.organism_name, r.project_data_type, r.project_methodtype].filter(Boolean),
76
+ summary: r.project_description,
77
+ href: `${BIOPROJECT}/${acc}`,
78
+ };
79
+ }
80
+ async function fetchNcbi(accession, db) {
81
+ const uid = await resolveUid(accession, db);
82
+ const json = await fetchJson(`${EUTILS}/esummary.fcgi?db=${db}&retmode=json&${TOOL}&id=${uid}`);
83
+ const record = json?.result?.[uid];
84
+ if (!record || record.error)
85
+ throw new Error(`No record for ${accession}.`);
86
+ return db === 'bioproject' ? toBioProjectCard(record, accession) : toGeoCard(record, accession);
87
+ }
88
+ function canonicalHref(accession, db) {
89
+ return db === 'bioproject' ? `${BIOPROJECT}/${accession}` : `${GEO_CGI}?acc=${accession}`;
90
+ }
91
+ function Badge({ children }) {
92
+ return (_jsx("span", { className: "inline-flex items-center px-3 py-1 ml-1 text-xs uppercase rounded-full border", children: children }));
93
+ }
94
+ function NcbiChild({ accession, db }) {
95
+ const { data, error } = useSWR(`${db}:${accession}`, () => fetchNcbi(accession, db), {
96
+ // NCBI records are effectively immutable; keep them cached and avoid extra requests against the rate limit.
97
+ revalidateOnFocus: false,
98
+ dedupingInterval: 60_000,
99
+ // Retry throttled responses with backoff; give up immediately on real failures (404, bad accession).
100
+ onErrorRetry: (err, _key, _config, revalidate, { retryCount }) => {
101
+ if (!(err instanceof RetryableError) || retryCount >= 5)
102
+ return;
103
+ setTimeout(() => revalidate({ retryCount }), 1500 * 2 ** retryCount);
104
+ },
105
+ });
106
+ if (!data && !error) {
107
+ return (_jsx("div", { className: "hover-document article w-[500px] sm:max-w-[500px] animate-pulse", children: "Loading..." }));
108
+ }
109
+ if (error || !data) {
110
+ return (_jsxs("div", { className: "hover-document article w-[500px] sm:max-w-[500px]", children: ["Error loading ", accession, "."] }));
111
+ }
112
+ return (_jsxs("div", { className: "hover-document article w-[500px] sm:max-w-[500px] p-3", children: [_jsxs("p", { className: "text-sm font-light", children: [data.kindLabel, " ", data.date && _jsxs("span", { children: ["\u00B7 ", data.date] })] }), _jsxs("div", { className: "mb-2 text-xl font-bold", children: [data.title || data.accession, " ", _jsx("code", { children: data.accession })] }), data.badges.length > 0 && (_jsx("div", { className: "flex flex-wrap mb-3 ml-1", children: data.badges.map((badge) => (_jsx(Badge, { children: badge }, badge))) })), data.summary && _jsx("p", { className: "text-md line-clamp-4", children: data.summary }), data.pubmedids && data.pubmedids.length > 0 && (_jsxs("div", { className: "mt-3 text-xs", children: ["Related:", ' ', data.pubmedids.map((pmid, i) => (_jsxs("span", { children: [i > 0 && ', ', _jsxs("a", { href: `https://pubmed.ncbi.nlm.nih.gov/${pmid}`, target: "_blank", rel: "noopener noreferrer", className: "link", children: ["PMID:", pmid] })] }, pmid)))] }))] }));
113
+ }
114
+ export function GEOLink({ accession, db = 'gds', children, className, }) {
115
+ return (_jsx(HoverPopover, { card: _jsx(NcbiChild, { accession: accession, db: db }), analytics: { cardKind: 'geo_link', properties: { accession, db } }, children: _jsx("a", { href: canonicalHref(accession, db), target: "_blank", rel: "noopener noreferrer", className: cn('hover-link', className), children: children }) }));
116
+ }
@@ -0,0 +1,15 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function GithubLink({ kind, children, url, org, repo, raw, file, from, to, issue_number, className, }: {
3
+ kind: 'file' | 'issue' | 'repo';
4
+ children: ReactNode;
5
+ url: string;
6
+ org: string;
7
+ repo: string;
8
+ raw?: string;
9
+ file?: string;
10
+ from?: number;
11
+ to?: number;
12
+ issue_number?: number;
13
+ className?: string;
14
+ }): import("react/jsx-runtime").JSX.Element;
15
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../../src/myst/links/github.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAiC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAyXtE,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,QAAQ,EACR,GAAG,EACH,GAAG,EACH,IAAI,EACJ,GAAG,EACH,IAAI,EACJ,IAAI,EACJ,EAAE,EACF,YAAY,EACZ,SAAS,GACV,EAAE;IACD,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAChC,QAAQ,EAAE,SAAS,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,2CAgDA"}