@myst-theme/site 0.1.38 → 0.2.1

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.38",
3
+ "version": "0.2.1",
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.1.38",
20
- "@myst-theme/frontmatter": "^0.1.38",
21
- "@myst-theme/jupyter": "^0.1.38",
22
- "@myst-theme/providers": "^0.1.38",
19
+ "@myst-theme/diagrams": "^0.2.1",
20
+ "@myst-theme/frontmatter": "^0.2.1",
21
+ "@myst-theme/jupyter": "^0.2.1",
22
+ "@myst-theme/providers": "^0.2.1",
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.1.38",
28
- "myst-to-react": "^0.1.38",
27
+ "myst-demo": "^0.2.1",
28
+ "myst-to-react": "^0.2.1",
29
29
  "nbtx": "^0.2.3",
30
30
  "node-cache": "^5.1.2",
31
31
  "node-fetch": "^2.6.7",
@@ -2,16 +2,16 @@ import classNames from 'classnames';
2
2
  import ArrowLeftIcon from '@heroicons/react/24/outline/ArrowLeftIcon';
3
3
  import ArrowRightIcon from '@heroicons/react/24/outline/ArrowRightIcon';
4
4
  import type { FooterLinks, NavigationLink } from '../types';
5
- import { useLinkProvider, useUrlbase, withUrlbase } from '@myst-theme/providers';
5
+ import { useLinkProvider, useBaseurl, withBaseurl } from '@myst-theme/providers';
6
6
 
7
7
  const FooterLink = ({ title, url, group, right }: NavigationLink & { right?: boolean }) => {
8
- const urlbase = useUrlbase();
8
+ const baseurl = useBaseurl();
9
9
  const Link = useLinkProvider();
10
10
  return (
11
11
  <Link
12
12
  prefetch="intent"
13
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"
14
- to={withUrlbase(url, urlbase)}
14
+ to={withBaseurl(url, baseurl)}
15
15
  >
16
16
  <div className="flex align-middle h-full">
17
17
  {right && (
@@ -1,8 +1,14 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
2
  import classNames from 'classnames';
3
- import { NavLink, useParams, useLocation, useNavigation } from '@remix-run/react';
3
+ import { useLocation, useNavigation } from '@remix-run/react';
4
4
  import type { SiteManifest } from 'myst-config';
5
- import { useNavOpen, useSiteManifest, useUrlbase, withUrlbase } from '@myst-theme/providers';
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 urlbase = useUrlbase();
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={withUrlbase(path, urlbase)}
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
@@ -141,10 +148,8 @@ export const TableOfContents = ({
141
148
  const footerRef = useRef<HTMLDivElement>(null);
142
149
  const [open] = useNavOpen();
143
150
  const config = useSiteManifest();
144
- const { folder, project } = useParams();
145
- const resolvedProjectSlug = projectSlug || (folder ?? project);
146
151
  if (!config) return null;
147
- const headings = getProjectHeadings(config, resolvedProjectSlug, {
152
+ const headings = getProjectHeadings(config, projectSlug, {
148
153
  addGroups: false,
149
154
  });
150
155
  useEffect(() => {
@@ -183,7 +188,7 @@ export const TableOfContents = ({
183
188
  aria-label="Table of Contents"
184
189
  className="flex-grow overflow-y-auto transition-opacity mt-6 pb-3 ml-3 xl:ml-0 mr-3"
185
190
  >
186
- <Headings folder={resolvedProjectSlug} headings={headings} sections={config?.projects} />
191
+ <Headings folder={projectSlug} headings={headings} sections={config?.projects} />
187
192
  </nav>
188
193
  {footer && (
189
194
  <div
@@ -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 { useLinkProvider, useNavOpen, useSiteManifest } from '@myst-theme/providers';
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;
@@ -178,11 +184,12 @@ function ActionMenu({ actions }: { actions?: SiteManifest['actions'] }) {
178
184
 
179
185
  function HomeLink({ logo, logoText, name }: { logo?: string; logoText?: string; name?: string }) {
180
186
  const Link = useLinkProvider();
187
+ const baseurl = useBaseurl();
181
188
  const nothingSet = !logo && !logoText;
182
189
  return (
183
190
  <Link
184
191
  className="flex items-center dark:text-white w-fit ml-3 md:ml-5 xl:ml-7"
185
- to="/"
192
+ to={withBaseurl('/', baseurl)}
186
193
  prefetch="intent"
187
194
  >
188
195
  {logo && (
@@ -16,7 +16,8 @@ export function getProject(
16
16
  config?: SiteManifest,
17
17
  projectSlug?: string,
18
18
  ): ManifestProject | undefined {
19
- if (!projectSlug || !config) return undefined;
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) {
@@ -0,0 +1,8 @@
1
+ export function Error404() {
2
+ return (
3
+ <>
4
+ <h1>No Site Found - 404</h1>
5
+ <p>No website is available at this url, or an error occurred. Please double check the url.</p>
6
+ </>
7
+ );
8
+ }
@@ -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,13 +8,13 @@ import {
8
8
  Outlet,
9
9
  Scripts,
10
10
  ScrollRestoration,
11
- useCatch,
12
11
  useLoaderData,
13
- Link as RemixLink,
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 { ErrorSiteNotFound } from './ErrorSiteNotFound';
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';
@@ -26,6 +26,8 @@ export function Document({
26
26
  config,
27
27
  title,
28
28
  scrollTopClass = 'scroll-p-20',
29
+ staticBuild,
30
+ baseurl,
29
31
  }: {
30
32
  children: React.ReactNode;
31
33
  scripts?: React.ReactNode;
@@ -33,7 +35,18 @@ export function Document({
33
35
  config?: SiteManifest;
34
36
  title?: 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,16 +61,18 @@ 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} Link={RemixLink as any}>
52
- <ThebeCoreProvider>
53
- <SiteProvider config={config}>
54
- <ConfiguredThebeServerProvider>{children}</ConfiguredThebeServerProvider>
55
- </SiteProvider>
56
- </ThebeCoreProvider>
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 />
75
+ {!staticBuild && <LiveReload />}
61
76
  {scripts}
62
77
  </body>
63
78
  </html>
@@ -74,37 +89,28 @@ export function App() {
74
89
  }
75
90
 
76
91
  export function AppWithReload() {
77
- const { theme, config, CONTENT_CDN_PORT } = useLoaderData<SiteLoader>();
92
+ const { theme, config, CONTENT_CDN_PORT, MODE, BASE_URL } = useLoaderData<SiteLoader>();
78
93
  return (
79
- <Document theme={theme} config={config} scripts={<ContentReload port={CONTENT_CDN_PORT} />}>
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
+ >
80
101
  <Outlet />
81
102
  </Document>
82
103
  );
83
104
  }
84
105
 
85
106
  export function AppCatchBoundary() {
86
- const caught = useCatch();
87
107
  return (
88
- <Document theme={Theme.light} title={caught.statusText}>
89
- <article className="content">
108
+ <Document theme={Theme.light}>
109
+ <article className="article">
90
110
  <main className="article-grid article-subgrid-gap col-screen">
91
- <ErrorSiteNotFound />
111
+ <Error404 />
92
112
  </main>
93
113
  </article>
94
114
  </Document>
95
115
  );
96
116
  }
97
-
98
- export function AppDebugErrorBoundary({ error }: { error: { message: string; stack: string } }) {
99
- return (
100
- <Document theme={Theme.light} title="Error">
101
- <div className="mt-16">
102
- <main className="article-grid article-subgrid-gap col-screen">
103
- <h1>An Error Occurred</h1>
104
- <code>{error.message}</code>
105
- <pre>{error.stack}</pre>
106
- </main>
107
- </div>
108
- </Document>
109
- );
110
- }
@@ -1,5 +1,5 @@
1
1
  export { ErrorProjectNotFound } from './ErrorProjectNotFound';
2
2
  export { ErrorDocumentNotFound } from './ErrorDocumentNotFound';
3
- export { ErrorSiteNotFound } from './ErrorSiteNotFound';
3
+ export { Error404 } from './Error404';
4
4
  export { ArticlePage, ArticlePageCatchBoundary, ProjectPageCatchBoundary } from './Article';
5
- export { App, AppWithReload, Document, AppCatchBoundary, AppDebugErrorBoundary } from './Root';
5
+ export { App, AppWithReload, Document, AppCatchBoundary } from './Root';
@@ -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}/${project.slug}/${page.slug}`);
151
+ .map((page) => `${baseurl}${projectSlug}/${page.slug}`);
151
152
  if (opts?.excludeIndex) return [...pages];
152
153
  return [
153
154
  opts?.explicitIndex
154
- ? `${baseurl}/${project.slug}/${project.index}`
155
- : `${baseurl}/${project.slug}`,
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&apos;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&apos;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&apos;s scientific writing, collaboration and publishing
26
- tools at <a href="https://curvenote.com">curvenote.com</a>.
27
- </p>
28
- </>
29
- );
30
- }