@myst-theme/site 0.1.37 → 0.2.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.
- package/package.json +7 -7
- package/src/components/ContentBlocks.tsx +8 -6
- package/src/components/ContentReload.tsx +6 -9
- package/src/components/DocumentOutline.tsx +11 -6
- package/src/components/ExternalOrInternalLink.tsx +2 -1
- package/src/components/FooterLinksBlock.tsx +4 -5
- package/src/components/Navigation/Loading.tsx +2 -2
- package/src/components/Navigation/TableOfContents.tsx +29 -9
- package/src/components/Navigation/TopNav.tsx +12 -3
- package/src/loaders/utils.ts +4 -3
- package/src/pages/Article.tsx +1 -5
- package/src/pages/Error404.tsx +8 -0
- package/src/pages/Root.tsx +46 -31
- package/src/pages/index.ts +2 -2
- package/src/seo/sitemap.ts +4 -3
- package/src/types.ts +3 -0
- package/src/pages/ErrorSiteNotFound.tsx +0 -30
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myst-theme/site",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"main": "./src/index.ts",
|
|
5
5
|
"types": "./src/index.ts",
|
|
6
6
|
"files": [
|
|
@@ -16,16 +16,16 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@headlessui/react": "^1.7.13",
|
|
18
18
|
"@heroicons/react": "^2.0.14",
|
|
19
|
-
"@myst-theme/diagrams": "^0.
|
|
20
|
-
"@myst-theme/frontmatter": "^0.
|
|
21
|
-
"@myst-theme/jupyter": "^0.
|
|
22
|
-
"@myst-theme/providers": "^0.
|
|
19
|
+
"@myst-theme/diagrams": "^0.2.0",
|
|
20
|
+
"@myst-theme/frontmatter": "^0.2.0",
|
|
21
|
+
"@myst-theme/jupyter": "^0.2.0",
|
|
22
|
+
"@myst-theme/providers": "^0.2.0",
|
|
23
23
|
"classnames": "^2.3.2",
|
|
24
24
|
"lodash.throttle": "^4.1.1",
|
|
25
25
|
"myst-common": "^0.0.16",
|
|
26
26
|
"myst-config": "^0.0.14",
|
|
27
|
-
"myst-demo": "^0.
|
|
28
|
-
"myst-to-react": "^0.
|
|
27
|
+
"myst-demo": "^0.2.0",
|
|
28
|
+
"myst-to-react": "^0.2.0",
|
|
29
29
|
"nbtx": "^0.2.3",
|
|
30
30
|
"node-cache": "^5.1.2",
|
|
31
31
|
"node-fetch": "^2.6.7",
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { useParse, DEFAULT_RENDERERS } from 'myst-to-react';
|
|
2
|
-
import
|
|
2
|
+
import { SourceFileKind } from 'myst-common';
|
|
3
|
+
import type { GenericParent } from 'myst-common';
|
|
3
4
|
import { useNodeRenderers } from '@myst-theme/providers';
|
|
4
5
|
import classNames from 'classnames';
|
|
5
6
|
import { ClearCell, RunCell } from './ComputeControls';
|
|
6
7
|
|
|
7
8
|
function isACodeCell(node: GenericParent) {
|
|
8
9
|
return (
|
|
10
|
+
node &&
|
|
9
11
|
node.type === 'block' &&
|
|
10
|
-
node.children
|
|
12
|
+
node.children &&
|
|
13
|
+
node.children?.length === 2 &&
|
|
11
14
|
node.children[0].type === 'code' &&
|
|
12
15
|
node.children[1].type === 'output'
|
|
13
16
|
);
|
|
@@ -50,16 +53,15 @@ function Block({
|
|
|
50
53
|
}
|
|
51
54
|
|
|
52
55
|
export function ContentBlocks({
|
|
53
|
-
name,
|
|
54
|
-
pageKind,
|
|
55
56
|
mdast,
|
|
57
|
+
pageKind = SourceFileKind.Article,
|
|
56
58
|
className,
|
|
57
59
|
}: {
|
|
58
|
-
name: string;
|
|
59
|
-
pageKind: SourceFileKind;
|
|
60
60
|
mdast: GenericParent;
|
|
61
|
+
pageKind?: SourceFileKind;
|
|
61
62
|
className?: string;
|
|
62
63
|
}) {
|
|
64
|
+
if (!mdast) return null;
|
|
63
65
|
const blocks = mdast.children as GenericParent[];
|
|
64
66
|
return (
|
|
65
67
|
<>
|
|
@@ -55,12 +55,9 @@ async function mystLiveReloadConnect(config: { onOpen?: () => void; port?: strin
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// Inspired by the LiveReload component in Remix
|
|
58
|
-
export
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}, []);
|
|
65
|
-
return null;
|
|
66
|
-
};
|
|
58
|
+
export function ContentReload({ port }: { port?: string | number }) {
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
mystLiveReloadConnect({ port });
|
|
61
|
+
}, []);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useNavigation } from '@remix-run/react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
3
|
import throttle from 'lodash.throttle';
|
|
4
|
-
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
4
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
5
5
|
|
|
6
6
|
const SELECTOR = [1, 2, 3, 4, 5, 6].map((n) => `main h${n}`).join(', ');
|
|
7
7
|
const HIGHLIGHT_CLASS = 'highlight';
|
|
@@ -176,7 +176,7 @@ export function useOutlineHeight<T extends HTMLElement = HTMLElement>() {
|
|
|
176
176
|
return () => {
|
|
177
177
|
window.removeEventListener('scroll', handleScroll);
|
|
178
178
|
};
|
|
179
|
-
}, [container, outline, transitionState]);
|
|
179
|
+
}, [container.current, outline.current, transitionState]);
|
|
180
180
|
return { container, outline };
|
|
181
181
|
}
|
|
182
182
|
|
|
@@ -191,13 +191,18 @@ export const DocumentOutline = ({
|
|
|
191
191
|
className?: string;
|
|
192
192
|
}) => {
|
|
193
193
|
const { activeId, headings, highlight } = useHeaders();
|
|
194
|
-
if (headings.length <= 1
|
|
194
|
+
if (headings.length <= 1 || !onClient) {
|
|
195
|
+
return <nav suppressHydrationWarning />;
|
|
196
|
+
}
|
|
195
197
|
return (
|
|
196
198
|
<nav
|
|
197
199
|
ref={outlineRef}
|
|
198
200
|
aria-label="Document Outline"
|
|
199
|
-
|
|
200
|
-
|
|
201
|
+
className={classNames(
|
|
202
|
+
'not-prose overflow-y-auto hidden',
|
|
203
|
+
'transition-opacity duration-700', // Animation on load
|
|
204
|
+
className,
|
|
205
|
+
)}
|
|
201
206
|
style={{
|
|
202
207
|
top: top ?? 0,
|
|
203
208
|
maxHeight: `calc(100vh - ${(top ?? 0) + 20}px)`,
|
|
@@ -206,7 +211,7 @@ export const DocumentOutline = ({
|
|
|
206
211
|
<div className="text-slate-900 mb-4 text-sm leading-6 dark:text-slate-100 uppercase">
|
|
207
212
|
In this article
|
|
208
213
|
</div>
|
|
209
|
-
|
|
214
|
+
<Headings headings={headings} activeId={activeId} highlight={highlight} />
|
|
210
215
|
</nav>
|
|
211
216
|
);
|
|
212
217
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useLinkProvider } from '@myst-theme/providers';
|
|
2
2
|
|
|
3
3
|
export function ExternalOrInternalLink({
|
|
4
4
|
to,
|
|
@@ -15,6 +15,7 @@ export function ExternalOrInternalLink({
|
|
|
15
15
|
title?: string;
|
|
16
16
|
children: React.ReactNode;
|
|
17
17
|
}) {
|
|
18
|
+
const Link = useLinkProvider();
|
|
18
19
|
if (to.startsWith('http') || isStatic) {
|
|
19
20
|
return (
|
|
20
21
|
<a href={to} className={className} target="_blank" rel="noopener noreferrer" title={title}>
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import { Link } from '@remix-run/react';
|
|
3
2
|
import ArrowLeftIcon from '@heroicons/react/24/outline/ArrowLeftIcon';
|
|
4
3
|
import ArrowRightIcon from '@heroicons/react/24/outline/ArrowRightIcon';
|
|
5
4
|
import type { FooterLinks, NavigationLink } from '../types';
|
|
6
|
-
import {
|
|
7
|
-
import { useEffect, useState } from 'react';
|
|
5
|
+
import { useLinkProvider, useBaseurl, withBaseurl } from '@myst-theme/providers';
|
|
8
6
|
|
|
9
7
|
const FooterLink = ({ title, url, group, right }: NavigationLink & { right?: boolean }) => {
|
|
10
|
-
const
|
|
8
|
+
const baseurl = useBaseurl();
|
|
9
|
+
const Link = useLinkProvider();
|
|
11
10
|
return (
|
|
12
11
|
<Link
|
|
13
12
|
prefetch="intent"
|
|
14
13
|
className="group flex-1 p-4 block border font-normal hover:border-blue-600 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"
|
|
15
|
-
to={
|
|
14
|
+
to={withBaseurl(url, baseurl)}
|
|
16
15
|
>
|
|
17
16
|
<div className="flex align-middle h-full">
|
|
18
17
|
{right && (
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useNavigation } from '@remix-run/react';
|
|
2
2
|
import { useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ import classNames from 'classnames';
|
|
|
6
6
|
* Show a loading progess bad if the load takes more than 150ms
|
|
7
7
|
*/
|
|
8
8
|
function useLoading() {
|
|
9
|
-
const transitionState =
|
|
9
|
+
const transitionState = useNavigation().state;
|
|
10
10
|
const ref = useMemo<{ start?: NodeJS.Timeout; finish?: NodeJS.Timeout }>(() => ({}), []);
|
|
11
11
|
const [showLoading, setShowLoading] = useState(false);
|
|
12
12
|
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import classNames from 'classnames';
|
|
3
|
-
import {
|
|
3
|
+
import { useLocation, useNavigation } from '@remix-run/react';
|
|
4
4
|
import type { SiteManifest } from 'myst-config';
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
useNavLinkProvider,
|
|
7
|
+
useNavOpen,
|
|
8
|
+
useSiteManifest,
|
|
9
|
+
useBaseurl,
|
|
10
|
+
withBaseurl,
|
|
11
|
+
} from '@myst-theme/providers';
|
|
6
12
|
import { getProjectHeadings } from '../../loaders';
|
|
7
13
|
import type { Heading } from '../../types';
|
|
8
14
|
|
|
@@ -26,8 +32,9 @@ const HeadingLink = ({
|
|
|
26
32
|
children: React.ReactNode;
|
|
27
33
|
}) => {
|
|
28
34
|
const { pathname } = useLocation();
|
|
35
|
+
const NavLink = useNavLinkProvider();
|
|
29
36
|
const exact = pathname === path;
|
|
30
|
-
const
|
|
37
|
+
const baseurl = useBaseurl();
|
|
31
38
|
const [, setOpen] = useNavOpen();
|
|
32
39
|
return (
|
|
33
40
|
<NavLink
|
|
@@ -44,7 +51,7 @@ const HeadingLink = ({
|
|
|
44
51
|
!isActive,
|
|
45
52
|
})
|
|
46
53
|
}
|
|
47
|
-
to={
|
|
54
|
+
to={withBaseurl(path, baseurl)}
|
|
48
55
|
suppressHydrationWarning // The pathname is not defined on the server always.
|
|
49
56
|
onClick={() => {
|
|
50
57
|
// Close the nav panel if it is open
|
|
@@ -138,14 +145,20 @@ export const TableOfContents = ({
|
|
|
138
145
|
projectSlug?: string;
|
|
139
146
|
footer?: React.ReactNode;
|
|
140
147
|
}) => {
|
|
148
|
+
const footerRef = useRef<HTMLDivElement>(null);
|
|
141
149
|
const [open] = useNavOpen();
|
|
142
150
|
const config = useSiteManifest();
|
|
143
|
-
const { folder, project } = useParams();
|
|
144
|
-
const resolvedProjectSlug = projectSlug || (folder ?? project);
|
|
145
151
|
if (!config) return null;
|
|
146
|
-
const headings = getProjectHeadings(config,
|
|
152
|
+
const headings = getProjectHeadings(config, projectSlug, {
|
|
147
153
|
addGroups: false,
|
|
148
154
|
});
|
|
155
|
+
useEffect(() => {
|
|
156
|
+
setTimeout(() => {
|
|
157
|
+
if (!footerRef.current) return;
|
|
158
|
+
footerRef.current.style.opacity = '1';
|
|
159
|
+
footerRef.current.style.transform = 'none';
|
|
160
|
+
}, 500);
|
|
161
|
+
}, [footerRef]);
|
|
149
162
|
if (!headings) return null;
|
|
150
163
|
return (
|
|
151
164
|
<div
|
|
@@ -175,9 +188,16 @@ export const TableOfContents = ({
|
|
|
175
188
|
aria-label="Table of Contents"
|
|
176
189
|
className="flex-grow overflow-y-auto transition-opacity mt-6 pb-3 ml-3 xl:ml-0 mr-3"
|
|
177
190
|
>
|
|
178
|
-
<Headings folder={
|
|
191
|
+
<Headings folder={projectSlug} headings={headings} sections={config?.projects} />
|
|
179
192
|
</nav>
|
|
180
|
-
{footer &&
|
|
193
|
+
{footer && (
|
|
194
|
+
<div
|
|
195
|
+
className="flex-none py-4 opacity-0 transition-all duration-700 translate-y-6"
|
|
196
|
+
ref={footerRef}
|
|
197
|
+
>
|
|
198
|
+
{footer}
|
|
199
|
+
</div>
|
|
200
|
+
)}
|
|
181
201
|
</div>
|
|
182
202
|
</div>
|
|
183
203
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NavLink } from '@remix-run/react';
|
|
2
2
|
import { Fragment } from 'react';
|
|
3
3
|
import classNames from 'classnames';
|
|
4
4
|
import { Menu, Transition } from '@headlessui/react';
|
|
@@ -7,7 +7,13 @@ import MenuIcon from '@heroicons/react/24/solid/Bars3Icon';
|
|
|
7
7
|
import ChevronDownIcon from '@heroicons/react/24/solid/ChevronDownIcon';
|
|
8
8
|
import type { SiteManifest, SiteNavItem } from 'myst-config';
|
|
9
9
|
import { ThemeButton } from './ThemeButton';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
useBaseurl,
|
|
12
|
+
useLinkProvider,
|
|
13
|
+
useNavOpen,
|
|
14
|
+
useSiteManifest,
|
|
15
|
+
withBaseurl,
|
|
16
|
+
} from '@myst-theme/providers';
|
|
11
17
|
import { LoadingBar } from './Loading';
|
|
12
18
|
|
|
13
19
|
export const DEFAULT_NAV_HEIGHT = 60;
|
|
@@ -25,6 +31,7 @@ function ExternalOrInternalLink({
|
|
|
25
31
|
nav?: boolean;
|
|
26
32
|
prefetch?: 'intent' | 'render' | 'none';
|
|
27
33
|
}) {
|
|
34
|
+
const Link = useLinkProvider();
|
|
28
35
|
const staticClass = typeof className === 'function' ? className({ isActive: false }) : className;
|
|
29
36
|
if (to.startsWith('http') || to.startsWith('mailto:')) {
|
|
30
37
|
return (
|
|
@@ -176,11 +183,13 @@ function ActionMenu({ actions }: { actions?: SiteManifest['actions'] }) {
|
|
|
176
183
|
}
|
|
177
184
|
|
|
178
185
|
function HomeLink({ logo, logoText, name }: { logo?: string; logoText?: string; name?: string }) {
|
|
186
|
+
const Link = useLinkProvider();
|
|
187
|
+
const baseurl = useBaseurl();
|
|
179
188
|
const nothingSet = !logo && !logoText;
|
|
180
189
|
return (
|
|
181
190
|
<Link
|
|
182
191
|
className="flex items-center dark:text-white w-fit ml-3 md:ml-5 xl:ml-7"
|
|
183
|
-
to=
|
|
192
|
+
to={withBaseurl('/', baseurl)}
|
|
184
193
|
prefetch="intent"
|
|
185
194
|
>
|
|
186
195
|
{logo && (
|
package/src/loaders/utils.ts
CHANGED
|
@@ -16,7 +16,8 @@ export function getProject(
|
|
|
16
16
|
config?: SiteManifest,
|
|
17
17
|
projectSlug?: string,
|
|
18
18
|
): ManifestProject | undefined {
|
|
19
|
-
if (!
|
|
19
|
+
if (!config) return undefined;
|
|
20
|
+
if (!projectSlug) return config.projects?.[0];
|
|
20
21
|
const project = config.projects?.find((p) => p.slug === projectSlug);
|
|
21
22
|
return project;
|
|
22
23
|
}
|
|
@@ -32,12 +33,12 @@ export function getProjectHeadings(
|
|
|
32
33
|
{
|
|
33
34
|
title: project.title,
|
|
34
35
|
slug: project.index,
|
|
35
|
-
path: `/${project.slug}
|
|
36
|
+
path: project.slug ? `/${project.slug}` : '/',
|
|
36
37
|
level: 'index',
|
|
37
38
|
},
|
|
38
39
|
...project.pages.map((p) => {
|
|
39
40
|
if (!('slug' in p)) return p;
|
|
40
|
-
return { ...p, path: `/${project.slug}/${p.slug}` };
|
|
41
|
+
return { ...p, path: projectSlug ? `/${project.slug}/${p.slug}` : `/${p.slug}` };
|
|
41
42
|
}),
|
|
42
43
|
];
|
|
43
44
|
if (opts.addGroups) {
|
package/src/pages/Article.tsx
CHANGED
|
@@ -37,11 +37,7 @@ export const ArticlePage = React.memo(function ({ article }: { article: PageLoad
|
|
|
37
37
|
</EnableCompute>
|
|
38
38
|
)}
|
|
39
39
|
</div>
|
|
40
|
-
<ContentBlocks
|
|
41
|
-
name={article.slug}
|
|
42
|
-
pageKind={article.kind}
|
|
43
|
-
mdast={article.mdast as GenericParent}
|
|
44
|
-
/>
|
|
40
|
+
<ContentBlocks pageKind={article.kind} mdast={article.mdast as GenericParent} />
|
|
45
41
|
<Bibliography />
|
|
46
42
|
{!hide_footer_links && <FooterLinksBlock links={article.footer} />}
|
|
47
43
|
</NotebookProvider>
|
package/src/pages/Root.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SiteManifest } from 'myst-config';
|
|
2
2
|
import type { SiteLoader } from '../types';
|
|
3
|
-
import { SiteProvider, Theme, ThemeProvider } from '@myst-theme/providers';
|
|
3
|
+
import { BaseUrlProvider, SiteProvider, Theme, ThemeProvider } from '@myst-theme/providers';
|
|
4
4
|
import {
|
|
5
5
|
Links,
|
|
6
6
|
LiveReload,
|
|
@@ -8,32 +8,45 @@ import {
|
|
|
8
8
|
Outlet,
|
|
9
9
|
Scripts,
|
|
10
10
|
ScrollRestoration,
|
|
11
|
-
useCatch,
|
|
12
11
|
useLoaderData,
|
|
13
|
-
Link
|
|
12
|
+
Link,
|
|
13
|
+
NavLink,
|
|
14
14
|
} from '@remix-run/react';
|
|
15
15
|
import { ContentReload, renderers } from '../components';
|
|
16
16
|
import { Analytics } from '../seo';
|
|
17
|
-
import {
|
|
17
|
+
import { Error404 } from './Error404';
|
|
18
18
|
import classNames from 'classnames';
|
|
19
19
|
import { ThebeCoreProvider } from 'thebe-react';
|
|
20
20
|
import { ConfiguredThebeServerProvider } from '@myst-theme/jupyter';
|
|
21
21
|
|
|
22
22
|
export function Document({
|
|
23
23
|
children,
|
|
24
|
+
scripts,
|
|
24
25
|
theme,
|
|
25
26
|
config,
|
|
26
27
|
title,
|
|
27
|
-
CONTENT_CDN_PORT,
|
|
28
28
|
scrollTopClass = 'scroll-p-20',
|
|
29
|
+
staticBuild,
|
|
30
|
+
baseurl,
|
|
29
31
|
}: {
|
|
30
32
|
children: React.ReactNode;
|
|
33
|
+
scripts?: React.ReactNode;
|
|
31
34
|
theme: Theme;
|
|
32
35
|
config?: SiteManifest;
|
|
33
36
|
title?: string;
|
|
34
|
-
CONTENT_CDN_PORT?: number | string;
|
|
35
37
|
scrollTopClass?: string;
|
|
38
|
+
staticBuild?: boolean;
|
|
39
|
+
baseurl?: string;
|
|
36
40
|
}) {
|
|
41
|
+
const links = staticBuild
|
|
42
|
+
? {
|
|
43
|
+
Link: (props: any) => <Link {...{ ...props, reloadDocument: true }} />,
|
|
44
|
+
NavLink: (props: any) => <NavLink {...{ ...props, reloadDocument: true }} />,
|
|
45
|
+
}
|
|
46
|
+
: {
|
|
47
|
+
Link: Link as any,
|
|
48
|
+
NavLink: NavLink as any,
|
|
49
|
+
};
|
|
37
50
|
return (
|
|
38
51
|
<html lang="en" className={classNames(theme, scrollTopClass)}>
|
|
39
52
|
<head>
|
|
@@ -48,54 +61,56 @@ export function Document({
|
|
|
48
61
|
/>
|
|
49
62
|
</head>
|
|
50
63
|
<body className="m-0 transition-colors duration-500 bg-white dark:bg-stone-900">
|
|
51
|
-
<ThemeProvider theme={theme} renderers={renderers}
|
|
52
|
-
<
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
64
|
+
<ThemeProvider theme={theme} renderers={renderers} {...links}>
|
|
65
|
+
<BaseUrlProvider baseurl={baseurl}>
|
|
66
|
+
<ThebeCoreProvider>
|
|
67
|
+
<SiteProvider config={config}>
|
|
68
|
+
<ConfiguredThebeServerProvider>{children}</ConfiguredThebeServerProvider>
|
|
69
|
+
</SiteProvider>
|
|
70
|
+
</ThebeCoreProvider>
|
|
71
|
+
</BaseUrlProvider>
|
|
57
72
|
</ThemeProvider>
|
|
58
73
|
<ScrollRestoration />
|
|
59
74
|
<Scripts />
|
|
60
|
-
<LiveReload />
|
|
61
|
-
|
|
75
|
+
{!staticBuild && <LiveReload />}
|
|
76
|
+
{scripts}
|
|
62
77
|
</body>
|
|
63
78
|
</html>
|
|
64
79
|
);
|
|
65
80
|
}
|
|
66
81
|
|
|
67
82
|
export function App() {
|
|
68
|
-
const { theme, config
|
|
83
|
+
const { theme, config } = useLoaderData<SiteLoader>();
|
|
69
84
|
return (
|
|
70
|
-
<Document theme={theme} config={config}
|
|
85
|
+
<Document theme={theme} config={config}>
|
|
71
86
|
<Outlet />
|
|
72
87
|
</Document>
|
|
73
88
|
);
|
|
74
89
|
}
|
|
75
90
|
|
|
76
|
-
export function
|
|
77
|
-
const
|
|
91
|
+
export function AppWithReload() {
|
|
92
|
+
const { theme, config, CONTENT_CDN_PORT, MODE, BASE_URL } = useLoaderData<SiteLoader>();
|
|
78
93
|
return (
|
|
79
|
-
<Document
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
94
|
+
<Document
|
|
95
|
+
theme={theme}
|
|
96
|
+
config={config}
|
|
97
|
+
scripts={MODE === 'static' ? undefined : <ContentReload port={CONTENT_CDN_PORT} />}
|
|
98
|
+
staticBuild={MODE === 'static'}
|
|
99
|
+
baseurl={BASE_URL}
|
|
100
|
+
>
|
|
101
|
+
<Outlet />
|
|
85
102
|
</Document>
|
|
86
103
|
);
|
|
87
104
|
}
|
|
88
105
|
|
|
89
|
-
export function
|
|
106
|
+
export function AppCatchBoundary() {
|
|
90
107
|
return (
|
|
91
|
-
<Document theme={Theme.light}
|
|
92
|
-
<
|
|
108
|
+
<Document theme={Theme.light}>
|
|
109
|
+
<article className="article">
|
|
93
110
|
<main className="article-grid article-subgrid-gap col-screen">
|
|
94
|
-
<
|
|
95
|
-
<code>{error.message}</code>
|
|
96
|
-
<pre>{error.stack}</pre>
|
|
111
|
+
<Error404 />
|
|
97
112
|
</main>
|
|
98
|
-
</
|
|
113
|
+
</article>
|
|
99
114
|
</Document>
|
|
100
115
|
);
|
|
101
116
|
}
|
package/src/pages/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { ErrorProjectNotFound } from './ErrorProjectNotFound';
|
|
2
2
|
export { ErrorDocumentNotFound } from './ErrorDocumentNotFound';
|
|
3
|
-
export {
|
|
3
|
+
export { Error404 } from './Error404';
|
|
4
4
|
export { ArticlePage, ArticlePageCatchBoundary, ProjectPageCatchBoundary } from './Article';
|
|
5
|
-
export { App, Document, AppCatchBoundary
|
|
5
|
+
export { App, AppWithReload, Document, AppCatchBoundary } from './Root';
|
package/src/seo/sitemap.ts
CHANGED
|
@@ -145,14 +145,15 @@ export function getSiteSlugs(
|
|
|
145
145
|
const slugs =
|
|
146
146
|
site.projects
|
|
147
147
|
?.map((project) => {
|
|
148
|
+
const projectSlug = project.slug ? `/${project.slug}` : '';
|
|
148
149
|
const pages = project.pages
|
|
149
150
|
.filter((page): page is ManifestProjectItem => 'slug' in page)
|
|
150
|
-
.map((page) => `${baseurl}
|
|
151
|
+
.map((page) => `${baseurl}${projectSlug}/${page.slug}`);
|
|
151
152
|
if (opts?.excludeIndex) return [...pages];
|
|
152
153
|
return [
|
|
153
154
|
opts?.explicitIndex
|
|
154
|
-
? `${baseurl}
|
|
155
|
-
: `${baseurl}
|
|
155
|
+
? `${baseurl}${projectSlug}/${project.index}`
|
|
156
|
+
: `${baseurl}${projectSlug}`,
|
|
156
157
|
...pages,
|
|
157
158
|
];
|
|
158
159
|
})
|
package/src/types.ts
CHANGED
|
@@ -17,6 +17,8 @@ export type SiteLoader = {
|
|
|
17
17
|
theme: Theme;
|
|
18
18
|
config?: SiteManifest;
|
|
19
19
|
CONTENT_CDN_PORT?: string | number;
|
|
20
|
+
MODE?: 'app' | 'static';
|
|
21
|
+
BASE_URL?: string;
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
export type NavigationLink = {
|
|
@@ -38,6 +40,7 @@ export type PageLoader = {
|
|
|
38
40
|
sha256: string;
|
|
39
41
|
slug: string;
|
|
40
42
|
domain: string; // This is written in at render time in the site
|
|
43
|
+
project: string; // This is written in at render time in the site
|
|
41
44
|
frontmatter: PageFrontmatter;
|
|
42
45
|
mdast: Root;
|
|
43
46
|
references: References;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export function ErrorSiteNotFound() {
|
|
2
|
-
return (
|
|
3
|
-
<>
|
|
4
|
-
<h1>No Site Found</h1>
|
|
5
|
-
<p>No website is available at this url, please double check the url.</p>
|
|
6
|
-
<h3>What's next?</h3>
|
|
7
|
-
<p>
|
|
8
|
-
If you are expecting to see <span className="font-semibold">your website</span> here and you
|
|
9
|
-
think that something has gone wrong, please send an email to{' '}
|
|
10
|
-
<a
|
|
11
|
-
href={`mailto:support@curvenote.com?subject=Website%20Unavailable&body=${encodeURIComponent(
|
|
12
|
-
`My website is not available. 😥`,
|
|
13
|
-
)}`}
|
|
14
|
-
>
|
|
15
|
-
support@curvenote.com
|
|
16
|
-
</a>
|
|
17
|
-
, or let us know on our <a href="https://slack.curvenote.dev">community slack</a>, and
|
|
18
|
-
we'll help out.
|
|
19
|
-
</p>
|
|
20
|
-
<p>
|
|
21
|
-
Or create a new temporary website from Markdown and Jupyter Notebooks using{' '}
|
|
22
|
-
<a href="https://try.curvenote.com">try.curvenote.com</a>.
|
|
23
|
-
</p>
|
|
24
|
-
<p>
|
|
25
|
-
Or find out more about Curvenote's scientific writing, collaboration and publishing
|
|
26
|
-
tools at <a href="https://curvenote.com">curvenote.com</a>.
|
|
27
|
-
</p>
|
|
28
|
-
</>
|
|
29
|
-
);
|
|
30
|
-
}
|