@myst-theme/site 0.1.28 → 0.1.30
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myst-theme/site",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.30",
|
|
4
4
|
"main": "./src/index.ts",
|
|
5
5
|
"types": "./src/index.ts",
|
|
6
6
|
"files": [
|
|
@@ -14,18 +14,18 @@
|
|
|
14
14
|
"lint:format": "prettier --check \"src/**/*.{ts,tsx,md}\""
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@headlessui/react": "^1.7.
|
|
17
|
+
"@headlessui/react": "^1.7.13",
|
|
18
18
|
"@heroicons/react": "^2.0.14",
|
|
19
|
-
"@myst-theme/diagrams": "^0.1.
|
|
20
|
-
"@myst-theme/frontmatter": "^0.1.
|
|
21
|
-
"@myst-theme/jupyter": "^0.1.
|
|
22
|
-
"@myst-theme/providers": "^0.1.
|
|
19
|
+
"@myst-theme/diagrams": "^0.1.30",
|
|
20
|
+
"@myst-theme/frontmatter": "^0.1.30",
|
|
21
|
+
"@myst-theme/jupyter": "^0.1.30",
|
|
22
|
+
"@myst-theme/providers": "^0.1.30",
|
|
23
23
|
"classnames": "^2.3.2",
|
|
24
24
|
"lodash.throttle": "^4.1.1",
|
|
25
25
|
"myst-common": "^0.0.14",
|
|
26
26
|
"myst-config": "^0.0.10",
|
|
27
|
-
"myst-demo": "^0.1.
|
|
28
|
-
"myst-to-react": "^0.1.
|
|
27
|
+
"myst-demo": "^0.1.30",
|
|
28
|
+
"myst-to-react": "^0.1.30",
|
|
29
29
|
"nbtx": "^0.2.3",
|
|
30
30
|
"node-cache": "^5.1.2",
|
|
31
31
|
"node-fetch": "^2.6.7",
|
|
@@ -12,15 +12,17 @@ export function Bibliography() {
|
|
|
12
12
|
const refs = hidden ? filtered.slice(0, HIDE_OVER_N_REFERENCES) : filtered;
|
|
13
13
|
return (
|
|
14
14
|
<section className="article-grid article-subgrid-gap col-screen">
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
<div>
|
|
16
|
+
{filtered.length > HIDE_OVER_N_REFERENCES && (
|
|
17
|
+
<button
|
|
18
|
+
onClick={() => setHidden(!hidden)}
|
|
19
|
+
className="float-right text-xs p-1 px-2 border rounded hover:border-blue-500 dark:hover:border-blue-400"
|
|
20
|
+
>
|
|
21
|
+
{hidden ? 'Show All' : 'Collapse'}
|
|
22
|
+
</button>
|
|
23
|
+
)}
|
|
24
|
+
<header className="text-lg font-semibold text-stone-900 dark:text-white">References</header>
|
|
25
|
+
</div>
|
|
24
26
|
<div className="text-xs mb-8 pl-3 text-stone-500 dark:text-stone-300">
|
|
25
27
|
<ol>
|
|
26
28
|
{refs.map((label) => {
|
|
@@ -171,13 +171,14 @@ export const DocumentOutline = ({
|
|
|
171
171
|
<nav
|
|
172
172
|
aria-label="Document Outline"
|
|
173
173
|
suppressHydrationWarning
|
|
174
|
-
className={classNames('not-prose transition-opacity', className)}
|
|
174
|
+
className={classNames('not-prose transition-opacity overflow-y-auto', className)}
|
|
175
175
|
style={{
|
|
176
176
|
top: top ?? 0,
|
|
177
177
|
height:
|
|
178
178
|
typeof document === 'undefined' || (height && height > window.innerHeight)
|
|
179
179
|
? undefined
|
|
180
180
|
: height,
|
|
181
|
+
maxHeight: `calc(100vh - ${(top ?? 0) + 20}px)`,
|
|
181
182
|
opacity: height && height > 300 ? undefined : 0,
|
|
182
183
|
pointerEvents: height && height > 300 ? undefined : 'none',
|
|
183
184
|
}}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fetch from 'node-fetch';
|
|
2
2
|
import NodeCache from 'node-cache';
|
|
3
|
-
import type { SiteManifest
|
|
3
|
+
import type { SiteManifest } from 'myst-config';
|
|
4
4
|
import { responseNoArticle, responseNoSite } from './errors.server';
|
|
5
5
|
import {
|
|
6
6
|
getFooterLinks,
|
|
@@ -21,7 +21,9 @@ declare global {
|
|
|
21
21
|
var cdnRouterCache: NodeCache | undefined, configCache: NodeCache | undefined;
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
export type Host = string | { CDN: string; id: string };
|
|
25
|
+
|
|
26
|
+
const DEFAULT_CDN = 'https://cdn.curvenote.com/';
|
|
25
27
|
|
|
26
28
|
function getCdnRouterCache() {
|
|
27
29
|
if (global.cdnRouterCache) return global.cdnRouterCache;
|
|
@@ -31,7 +33,7 @@ function getCdnRouterCache() {
|
|
|
31
33
|
return global.cdnRouterCache;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
|
-
function getConfigCache() {
|
|
36
|
+
export function getConfigCache() {
|
|
35
37
|
if (global.configCache) return global.configCache;
|
|
36
38
|
console.log('Creating configCache');
|
|
37
39
|
// The config can be long lived as it is static (0 == ∞)
|
|
@@ -52,9 +54,27 @@ async function getCdnPath(hostname: string): Promise<string | undefined> {
|
|
|
52
54
|
return data.cdn;
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
function
|
|
57
|
+
function withPublicFolderUrl(baseUrl: string, url: string): string {
|
|
58
|
+
return withBaseUrl(baseUrl, `public${url}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export async function getCdnLocation(host: Host) {
|
|
62
|
+
if (typeof host === 'string') {
|
|
63
|
+
const id = await getCdnPath(host);
|
|
64
|
+
if (!id) throw responseNoSite();
|
|
65
|
+
return { CDN: DEFAULT_CDN, id };
|
|
66
|
+
}
|
|
67
|
+
return host;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function getCdnBaseUrl(host: Host): Promise<string> {
|
|
71
|
+
const { CDN, id } = await getCdnLocation(host);
|
|
72
|
+
return `${CDN}${id}/`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function withBaseUrl<T extends string | undefined>(baseUrl: string, url: T): T {
|
|
56
76
|
if (!url) return url;
|
|
57
|
-
return `${
|
|
77
|
+
return `${baseUrl}${url}` as T;
|
|
58
78
|
}
|
|
59
79
|
|
|
60
80
|
/**
|
|
@@ -67,7 +87,7 @@ function foldTitleString(title?: string): string | undefined {
|
|
|
67
87
|
/**
|
|
68
88
|
* If the site title and the first nav item are the same, remove it.
|
|
69
89
|
*/
|
|
70
|
-
function removeSingleNavItems(config:
|
|
90
|
+
function removeSingleNavItems(config: SiteManifest) {
|
|
71
91
|
if (
|
|
72
92
|
config?.nav?.length === 1 &&
|
|
73
93
|
foldTitleString(config.nav[0].title) === foldTitleString(config.title)
|
|
@@ -76,48 +96,51 @@ function removeSingleNavItems(config: Config) {
|
|
|
76
96
|
}
|
|
77
97
|
}
|
|
78
98
|
|
|
79
|
-
export async function getConfig(
|
|
80
|
-
const
|
|
99
|
+
export async function getConfig(host: Host): Promise<SiteManifest> {
|
|
100
|
+
const location = await getCdnLocation(host);
|
|
101
|
+
const baseUrl = await getCdnBaseUrl(location);
|
|
102
|
+
const { id } = location;
|
|
81
103
|
if (!id) throw responseNoSite();
|
|
82
|
-
const cached = getConfigCache().get<
|
|
104
|
+
const cached = getConfigCache().get<SiteManifest>(id);
|
|
83
105
|
// Load the data from an in memory cache.
|
|
84
106
|
if (cached) return cached;
|
|
85
|
-
const response = await fetch(
|
|
107
|
+
const response = await fetch(withBaseUrl(baseUrl, 'config.json'));
|
|
86
108
|
if (response.status === 404) throw responseNoSite();
|
|
87
|
-
const data = (await response.json()) as
|
|
109
|
+
const data = (await response.json()) as SiteManifest;
|
|
88
110
|
data.id = id;
|
|
89
111
|
removeSingleNavItems(data);
|
|
90
|
-
updateSiteManifestStaticLinksInplace(data, (url) =>
|
|
91
|
-
getConfigCache().set<
|
|
112
|
+
updateSiteManifestStaticLinksInplace(data, (url) => withPublicFolderUrl(baseUrl, url));
|
|
113
|
+
getConfigCache().set<SiteManifest>(id, data);
|
|
92
114
|
return data;
|
|
93
115
|
}
|
|
94
116
|
|
|
95
|
-
export async function getObjectsInv(
|
|
96
|
-
const
|
|
97
|
-
if (!
|
|
98
|
-
const url = `${
|
|
117
|
+
export async function getObjectsInv(host: Host): Promise<Buffer | undefined> {
|
|
118
|
+
const baseUrl = await getCdnBaseUrl(host);
|
|
119
|
+
if (!baseUrl) return;
|
|
120
|
+
const url = `${baseUrl}objects.inv`;
|
|
99
121
|
const response = await fetch(url);
|
|
100
122
|
if (response.status === 404) return;
|
|
101
123
|
const buffer = await response.buffer();
|
|
102
124
|
return buffer;
|
|
103
125
|
}
|
|
104
126
|
|
|
105
|
-
|
|
106
|
-
|
|
127
|
+
async function getData(
|
|
128
|
+
baseUrl: string,
|
|
129
|
+
config?: SiteManifest,
|
|
107
130
|
project?: string,
|
|
108
131
|
slug?: string,
|
|
109
132
|
): Promise<PageLoader | null> {
|
|
110
133
|
if (!project || !slug || !config) throw responseNoArticle();
|
|
111
134
|
const { id } = config;
|
|
112
135
|
if (!id) throw responseNoSite();
|
|
113
|
-
const response = await fetch(
|
|
136
|
+
const response = await fetch(withBaseUrl(baseUrl, `content/${project}/${slug}.json`));
|
|
114
137
|
if (response.status === 404) throw responseNoArticle();
|
|
115
138
|
const data = (await response.json()) as PageLoader;
|
|
116
|
-
return updatePageStaticLinksInplace(data, (url) =>
|
|
139
|
+
return updatePageStaticLinksInplace(data, (url) => withPublicFolderUrl(baseUrl, url));
|
|
117
140
|
}
|
|
118
141
|
|
|
119
142
|
export async function getPage(
|
|
120
|
-
|
|
143
|
+
host: Host,
|
|
121
144
|
opts: {
|
|
122
145
|
domain?: string;
|
|
123
146
|
project?: string;
|
|
@@ -127,7 +150,8 @@ export async function getPage(
|
|
|
127
150
|
},
|
|
128
151
|
): Promise<PageLoader | Response | null> {
|
|
129
152
|
const projectName = opts.project;
|
|
130
|
-
const
|
|
153
|
+
const baseUrl = await getCdnBaseUrl(host);
|
|
154
|
+
const config = await getConfig(host);
|
|
131
155
|
if (!config) throw responseNoSite();
|
|
132
156
|
const project = getProject(config, projectName);
|
|
133
157
|
if (!project) throw responseNoArticle();
|
|
@@ -135,7 +159,7 @@ export async function getPage(
|
|
|
135
159
|
return redirect(`${typeof opts.redirect === 'string' ? opts.redirect : '/'}${projectName}`);
|
|
136
160
|
}
|
|
137
161
|
const slug = opts.loadIndexPage || opts.slug == null ? project.index : opts.slug;
|
|
138
|
-
const loader = await getData(config, projectName, slug).catch((e) => {
|
|
162
|
+
const loader = await getData(baseUrl, config, projectName, slug).catch((e) => {
|
|
139
163
|
console.error(e);
|
|
140
164
|
return null;
|
|
141
165
|
});
|