@myst-theme/site 0.3.2 → 0.3.4

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.3.2",
3
+ "version": "0.3.4",
4
4
  "main": "./src/index.ts",
5
5
  "types": "./src/index.ts",
6
6
  "files": [
@@ -16,20 +16,20 @@
16
16
  "dependencies": {
17
17
  "@headlessui/react": "^1.7.15",
18
18
  "@heroicons/react": "^2.0.18",
19
- "@myst-theme/diagrams": "^0.3.2",
20
- "@myst-theme/frontmatter": "^0.3.2",
21
- "@myst-theme/jupyter": "^0.3.2",
22
- "@myst-theme/providers": "^0.3.2",
19
+ "@myst-theme/diagrams": "^0.3.4",
20
+ "@myst-theme/frontmatter": "^0.3.4",
21
+ "@myst-theme/jupyter": "^0.3.4",
22
+ "@myst-theme/providers": "^0.3.4",
23
23
  "classnames": "^2.3.2",
24
24
  "lodash.throttle": "^4.1.1",
25
25
  "myst-common": "^1.0.1",
26
26
  "myst-config": "^1.0.0",
27
- "myst-demo": "^0.3.2",
28
- "myst-to-react": "^0.3.2",
27
+ "myst-demo": "^0.3.4",
28
+ "myst-to-react": "^0.3.4",
29
29
  "nbtx": "^0.2.3",
30
30
  "node-cache": "^5.1.2",
31
31
  "node-fetch": "^2.6.11",
32
- "thebe-react": "^0.2.3",
32
+ "thebe-react": "^0.2.7",
33
33
  "unist-util-select": "^4.0.1"
34
34
  },
35
35
  "peerDependencies": {
@@ -16,14 +16,14 @@ export function Bibliography() {
16
16
  {filtered.length > HIDE_OVER_N_REFERENCES && (
17
17
  <button
18
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"
19
+ className="float-right p-1 px-2 text-xs border rounded hover:border-blue-500 dark:hover:border-blue-400"
20
20
  >
21
21
  {hidden ? 'Show All' : 'Collapse'}
22
22
  </button>
23
23
  )}
24
24
  <header className="text-lg font-semibold text-stone-900 dark:text-white">References</header>
25
25
  </div>
26
- <div className="text-xs mb-8 pl-3 text-stone-500 dark:text-stone-300">
26
+ <div className="pl-3 mb-8 text-xs text-stone-500 dark:text-stone-300">
27
27
  <ol>
28
28
  {refs.map((label) => {
29
29
  const { html } = data[label];
@@ -37,7 +37,7 @@ export function Bibliography() {
37
37
  );
38
38
  })}
39
39
  {filtered.length > HIDE_OVER_N_REFERENCES && (
40
- <li className="list-none text-center">
40
+ <li className="text-center list-none">
41
41
  <button
42
42
  onClick={() => setHidden(!hidden)}
43
43
  className="p-2 border rounded hover:border-blue-500 dark:hover:border-blue-400"
@@ -3,7 +3,11 @@ import { SourceFileKind } from 'myst-common';
3
3
  import type { GenericParent } from 'myst-common';
4
4
  import { useNodeRenderers } from '@myst-theme/providers';
5
5
  import classNames from 'classnames';
6
- import { ClearCell, RunCell } from './ComputeControls';
6
+ import {
7
+ NotebookClearCell,
8
+ NotebookRunCell,
9
+ NotebookRunCellSpinnerOnly,
10
+ } from '@myst-theme/jupyter';
7
11
 
8
12
  function isACodeCell(node: GenericParent) {
9
13
  return (
@@ -18,6 +22,7 @@ function isACodeCell(node: GenericParent) {
18
22
 
19
23
  function Block({
20
24
  id,
25
+ pageKind,
21
26
  node,
22
27
  className,
23
28
  }: {
@@ -41,13 +46,22 @@ function Block({
41
46
  [subGrid]: !noSubGrid,
42
47
  })}
43
48
  >
44
- {children}
45
- {isACodeCell(node) && (
46
- <div className="hidden group-hover/block:flex md:flex-col absolute -top-[28px] md:top-0 right-0 md:-right-[28px] mt-8">
47
- <RunCell id={id} />
48
- <ClearCell id={id} />
49
- </div>
49
+ {pageKind === SourceFileKind.Notebook && isACodeCell(node) && (
50
+ <>
51
+ <div className="flex sticky top-[80px] z-10 opacity-70 group-hover/block:opacity-100 group-hover/block:hidden">
52
+ <div className="absolute top-0 -right-[28px] flex md:flex-col">
53
+ <NotebookRunCellSpinnerOnly id={id} />
54
+ </div>
55
+ </div>
56
+ <div className="hidden sticky top-[80px] z-10 opacity-70 group-hover/block:opacity-100 group-hover/block:flex">
57
+ <div className="absolute top-0 -right-[28px] flex md:flex-col">
58
+ <NotebookRunCell id={id} />
59
+ <NotebookClearCell id={id} />
60
+ </div>
61
+ </div>
62
+ </>
50
63
  )}
64
+ {children}
51
65
  </div>
52
66
  );
53
67
  }
@@ -26,7 +26,7 @@ type Props = {
26
26
  * scrollIntoView is used to ensure that when a user clicks on an item, it will smoothly scroll.
27
27
  */
28
28
  const Headings = ({ headings, activeId, highlight, selector }: Props) => (
29
- <ul className="text-slate-400 text-sm leading-6">
29
+ <ul className="text-sm text-slate-400 leading-6">
30
30
  {headings.map((heading) => (
31
31
  <li
32
32
  key={heading.id}
@@ -213,7 +213,7 @@ export const DocumentOutline = ({
213
213
  maxHeight: `calc(100vh - ${(top ?? 0) + 20}px)`,
214
214
  }}
215
215
  >
216
- <div className="text-slate-900 mb-4 text-sm leading-6 dark:text-slate-100 uppercase">
216
+ <div className="mb-4 text-sm uppercase text-slate-900 leading-6 dark:text-slate-100">
217
217
  In this article
218
218
  </div>
219
219
  <Headings headings={headings} activeId={activeId} highlight={highlight} selector={selector} />
@@ -16,19 +16,19 @@ const FooterLink = ({
16
16
  return (
17
17
  <Link
18
18
  prefetch="intent"
19
- 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"
19
+ className="flex-1 block p-4 font-normal text-gray-600 no-underline border border-gray-200 rounded group hover:border-blue-600 dark:hover:border-blue-400 hover:text-blue-600 dark:hover:text-blue-400 dark:text-gray-100 dark:border-gray-500 shadow-sm hover:shadow-lg dark:shadow-neutral-700"
20
20
  to={withBaseurl(url, baseurl)}
21
21
  >
22
- <div className="flex align-middle h-full">
22
+ <div className="flex h-full align-middle">
23
23
  {right && (
24
- <ArrowLeftIcon className="w-6 h-6 self-center transition-transform group-hover:-translate-x-1 shrink-0" />
24
+ <ArrowLeftIcon className="self-center w-6 h-6 transition-transform group-hover:-translate-x-1 shrink-0" />
25
25
  )}
26
26
  <div className={classNames('flex-grow', { 'text-right': right })}>
27
27
  <div className="text-xs text-gray-500 dark:text-gray-400">{group || ' '}</div>
28
28
  {short_title || title}
29
29
  </div>
30
30
  {!right && (
31
- <ArrowRightIcon className="w-6 h-6 self-center transition-transform group-hover:translate-x-1 shrink-0" />
31
+ <ArrowRightIcon className="self-center w-6 h-6 transition-transform group-hover:translate-x-1 shrink-0" />
32
32
  )}
33
33
  </div>
34
34
  </Link>
@@ -38,7 +38,7 @@ const FooterLink = ({
38
38
  export function FooterLinksBlock({ links }: { links?: FooterLinks }) {
39
39
  if (!links) return null;
40
40
  return (
41
- <div className="flex space-x-4 pt-10 mb-10">
41
+ <div className="flex pt-10 mb-10 space-x-4">
42
42
  {links.navigation?.prev && <FooterLink {...links.navigation?.prev} right />}
43
43
  {links.navigation?.next && <FooterLink {...links.navigation?.next} />}
44
44
  </div>
@@ -48,7 +48,7 @@ const HeadingLink = ({
48
48
  'border-l pl-4 text-blue-500 border-current dark:text-blue-400': !isIndex && isActive,
49
49
  'font-semibold': isActive,
50
50
  'border-l pl-4 border-transparent hover:border-slate-400 dark:hover:border-slate-500 text-slate-700 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-300':
51
- !isActive,
51
+ !isIndex && !isActive,
52
52
  })
53
53
  }
54
54
  to={withBaseurl(path, baseurl)}
@@ -92,7 +92,7 @@ const Headings = ({ folder, headings, sections }: Props) => {
92
92
  {heading.short_title || heading.title}
93
93
  </HeadingLink>
94
94
  ) : (
95
- <h5 className="mb-3 lg:mt-8 font-semibold break-words dark:text-white">
95
+ <h5 className="mb-3 font-semibold break-words lg:mt-8 dark:text-white">
96
96
  {heading.short_title || heading.title}
97
97
  </h5>
98
98
  )}
@@ -191,7 +191,7 @@ export const TableOfContents = ({
191
191
  </nav>
192
192
  {footer && (
193
193
  <div
194
- className="flex-none py-4 opacity-0 transition-all duration-700 translate-y-6"
194
+ className="flex-none py-4 transition-all duration-700 translate-y-6 opacity-0"
195
195
  ref={footerRef}
196
196
  >
197
197
  {footer}
@@ -3,7 +3,7 @@ import MoonIcon from '@heroicons/react/24/solid/MoonIcon';
3
3
  import SunIcon from '@heroicons/react/24/outline/SunIcon';
4
4
  import classNames from 'classnames';
5
5
 
6
- export function ThemeButton({ className = 'mx-3 h-8 w-8' }: { className?: string }) {
6
+ export function ThemeButton({ className = 'w-8 h-8 mx-3' }: { className?: string }) {
7
7
  const { isDark, nextTheme } = useTheme();
8
8
  return (
9
9
  <button
@@ -57,7 +57,7 @@ function ExternalOrInternalLink({
57
57
  function NavItem({ item }: { item: SiteNavItem }) {
58
58
  if (!('children' in item)) {
59
59
  return (
60
- <div className="relative grow-0 inline-block mx-2">
60
+ <div className="relative inline-block mx-2 grow-0">
61
61
  <ExternalOrInternalLink
62
62
  nav
63
63
  to={item.url ?? ''}
@@ -76,9 +76,9 @@ function NavItem({ item }: { item: SiteNavItem }) {
76
76
  );
77
77
  }
78
78
  return (
79
- <Menu as="div" className="relative grow-0 inline-block mx-2">
79
+ <Menu as="div" className="relative inline-block mx-2 grow-0">
80
80
  <div className="inline-block">
81
- <Menu.Button className="inline-flex items-center justify-center w-full mx-2 py-1 text-md font-medium text-stone-900 dark:text-white rounded-md focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75">
81
+ <Menu.Button className="inline-flex items-center justify-center w-full py-1 mx-2 font-medium rounded-md text-md text-stone-900 dark:text-white focus:outline-none focus-visible:ring-2 focus-visible:ring-white focus-visible:ring-opacity-75">
82
82
  <span>{item.title}</span>
83
83
  <ChevronDownIcon className="w-5 h-5 ml-2 -mr-1 text-violet-200 hover:text-violet-100" />
84
84
  </Menu.Button>
@@ -92,7 +92,7 @@ function NavItem({ item }: { item: SiteNavItem }) {
92
92
  leaveFrom="transform opacity-100 scale-100"
93
93
  leaveTo="transform opacity-0 scale-95"
94
94
  >
95
- <Menu.Items className="origin-top-left absolute left-4 mt-2 w-48 rounded-sm shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
95
+ <Menu.Items className="absolute w-48 py-1 mt-2 origin-top-left bg-white rounded-sm shadow-lg left-4 ring-1 ring-black ring-opacity-5 focus:outline-none">
96
96
  {item.children?.map((action) => (
97
97
  <Menu.Item key={action.url}>
98
98
  {/* This is really ugly, BUT, the action needs to be defined HERE or the click away doesn't work for some reason */}
@@ -131,7 +131,7 @@ function NavItem({ item }: { item: SiteNavItem }) {
131
131
  function NavItems({ nav }: { nav?: SiteManifest['nav'] }) {
132
132
  if (!nav) return null;
133
133
  return (
134
- <div className="text-md flex-grow hidden lg:block">
134
+ <div className="flex-grow hidden text-md lg:block">
135
135
  {nav.map((item) => {
136
136
  return <NavItem key={'url' in item ? item.url : item.title} item={item} />;
137
137
  })}
@@ -144,10 +144,10 @@ function ActionMenu({ actions }: { actions?: SiteManifest['actions'] }) {
144
144
  return (
145
145
  <Menu as="div" className="relative">
146
146
  <div>
147
- <Menu.Button className="bg-transparent flex text-sm rounded-full focus:outline-none">
147
+ <Menu.Button className="flex text-sm bg-transparent rounded-full focus:outline-none">
148
148
  <span className="sr-only">Open Menu</span>
149
149
  <div className="flex items-center text-stone-200 hover:text-white">
150
- <EllipsisVerticalIcon className="h-8 w-8 p-1" />
150
+ <EllipsisVerticalIcon className="w-8 h-8 p-1" />
151
151
  </div>
152
152
  </Menu.Button>
153
153
  </div>
@@ -160,7 +160,7 @@ function ActionMenu({ actions }: { actions?: SiteManifest['actions'] }) {
160
160
  leaveFrom="transform opacity-100 scale-100"
161
161
  leaveTo="transform opacity-0 scale-95"
162
162
  >
163
- <Menu.Items className="origin-top-right absolute right-0 mt-2 w-48 rounded-sm shadow-lg py-1 bg-white ring-1 ring-black ring-opacity-5 focus:outline-none">
163
+ <Menu.Items className="absolute right-0 w-48 py-1 mt-2 origin-top-right bg-white rounded-sm shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
164
164
  {actions?.map((action) => (
165
165
  <Menu.Item key={action.url}>
166
166
  {({ active }) => (
@@ -182,19 +182,46 @@ function ActionMenu({ actions }: { actions?: SiteManifest['actions'] }) {
182
182
  );
183
183
  }
184
184
 
185
- function HomeLink({ logo, logoText, name }: { logo?: string; logoText?: string; name?: string }) {
185
+ function HomeLink({
186
+ logo,
187
+ logoDark,
188
+ logoText,
189
+ name,
190
+ }: {
191
+ logo?: string;
192
+ logoDark?: string;
193
+ logoText?: string;
194
+ name?: string;
195
+ }) {
186
196
  const Link = useLinkProvider();
187
197
  const baseurl = useBaseurl();
188
198
  const nothingSet = !logo && !logoText;
189
199
  return (
190
200
  <Link
191
- className="flex items-center dark:text-white w-fit ml-3 md:ml-5 xl:ml-7"
201
+ className="flex items-center ml-3 dark:text-white w-fit md:ml-5 xl:ml-7"
192
202
  to={withBaseurl('/', baseurl)}
193
203
  prefetch="intent"
194
204
  >
195
205
  {logo && (
196
- <div className="dark:bg-white dark:rounded p-1 mr-3">
197
- <img src={logo} className="h-9" alt={logoText || name} height="2.25rem"></img>
206
+ <div
207
+ className={classNames('p-1 mr-3', {
208
+ 'dark:bg-white dark:rounded': !logoDark,
209
+ })}
210
+ >
211
+ <img
212
+ src={logo}
213
+ className={classNames('h-9', { 'dark:hidden': !!logoDark })}
214
+ alt={logoText || name}
215
+ height="2.25rem"
216
+ ></img>
217
+ {logoDark && (
218
+ <img
219
+ src={logoDark}
220
+ className="hidden h-9 dark:block"
221
+ alt={logoText || name}
222
+ height="2.25rem"
223
+ ></img>
224
+ )}
198
225
  </div>
199
226
  )}
200
227
  <span
@@ -211,7 +238,8 @@ function HomeLink({ logo, logoText, name }: { logo?: string; logoText?: string;
211
238
  export function TopNav() {
212
239
  const [open, setOpen] = useNavOpen();
213
240
  const config = useSiteManifest();
214
- const { logo, logo_text, logoText, actions, title, nav } = config ?? ({} as SiteManifest);
241
+ const { logo, logo_dark, logo_text, logoText, actions, title, nav } =
242
+ config ?? ({} as SiteManifest);
215
243
  return (
216
244
  <div className="bg-white/80 backdrop-blur dark:bg-stone-900/80 shadow dark:shadow-stone-700 p-3 md:px-8 fixed w-screen top-0 z-30 h-[60px]">
217
245
  <nav className="flex items-center justify-between flex-wrap max-w-[1440px] mx-auto">
@@ -223,15 +251,20 @@ export function TopNav() {
223
251
  setOpen(!open);
224
252
  }}
225
253
  >
226
- <MenuIcon className="h-8 w-8 p-1" />
254
+ <MenuIcon className="w-8 h-8 p-1" />
227
255
  <span className="sr-only">Open Menu</span>
228
256
  </button>
229
257
  </div>
230
- <HomeLink name={title} logo={logo} logoText={logo_text || logoText} />
258
+ <HomeLink
259
+ name={title}
260
+ logo={logo}
261
+ logoDark={logo_dark}
262
+ logoText={logo_text || logoText}
263
+ />
231
264
  </div>
232
- <div className="flex-grow flex items-center w-auto">
265
+ <div className="flex items-center flex-grow w-auto">
233
266
  <NavItems nav={nav} />
234
- <div className="block flex-grow"></div>
267
+ <div className="flex-grow block"></div>
235
268
  <ThemeButton />
236
269
  <div className="block sm:hidden">
237
270
  <ActionMenu actions={actions} />
@@ -240,7 +273,7 @@ export function TopNav() {
240
273
  {actions?.map((action, index) => (
241
274
  <ExternalOrInternalLink
242
275
  key={action.url || index}
243
- className="inline-block text-md px-4 py-2 mx-1 leading-none border rounded border-stone-700 dark:border-white text-stone-700 dark:text-white hover:text-stone-500 dark:hover:text-neutral-800 hover:bg-neutral-100 mt-0"
276
+ className="inline-block px-4 py-2 mx-1 mt-0 leading-none border rounded text-md border-stone-700 dark:border-white text-stone-700 dark:text-white hover:text-stone-500 dark:hover:text-neutral-800 hover:bg-neutral-100"
244
277
  to={action.url}
245
278
  >
246
279
  {action.title}
@@ -96,7 +96,9 @@ export function updateSiteManifestStaticLinksInplace(
96
96
  if (!action.static) return;
97
97
  action.url = updateUrl(action.url);
98
98
  });
99
+ // TODO: this needs to be based on the template.yml in the future
99
100
  if (data.logo) data.logo = updateUrl(data.logo);
101
+ if (data.logo_dark) data.logo_dark = updateUrl(data.logo_dark);
100
102
  // Update the thumbnails to point at the CDN
101
103
  data.projects?.forEach((project) => {
102
104
  project.pages
@@ -1,49 +1,52 @@
1
1
  import React from 'react';
2
2
  import { ReferencesProvider } from '@myst-theme/providers';
3
- import { FrontmatterBlock } from '@myst-theme/frontmatter';
4
3
  import { Bibliography, ContentBlocks, FooterLinksBlock } from '../components';
5
4
  import { ErrorDocumentNotFound } from './ErrorDocumentNotFound';
6
5
  import { ErrorProjectNotFound } from './ErrorProjectNotFound';
7
6
  import type { PageLoader } from '../types';
8
- import { ThebeRenderMimeRegistryProvider, ThebeSessionProvider } from 'thebe-react';
9
7
  import type { GenericParent } from 'myst-common';
10
8
  import { SourceFileKind } from 'myst-common';
11
- import { EnableCompute } from '../components/EnableCompute';
12
- import { NotebookRunAll } from '../components/ComputeControls';
13
- import { NotebookProvider, BinderBadge, useComputeOptions } from '@myst-theme/jupyter';
9
+ import {
10
+ useComputeOptions,
11
+ ExecuteScopeProvider,
12
+ BusyScopeProvider,
13
+ NotebookToolbar,
14
+ ConnectionStatusTray,
15
+ BinderBadge,
16
+ } from '@myst-theme/jupyter';
17
+ import { FrontmatterBlock } from '@myst-theme/frontmatter';
14
18
 
15
19
  export const ArticlePage = React.memo(function ({ article }: { article: PageLoader }) {
16
20
  const computeOptions = useComputeOptions();
17
21
  const canCompute = computeOptions.canCompute && (article.frontmatter as any)?.thebe !== false;
18
- const { hide_title_block, hide_footer_links, binder } =
19
- (article.frontmatter as any)?.design ?? {};
20
- const isJupyter = article?.kind && article.kind === SourceFileKind.Notebook;
22
+ const { hide_title_block, hide_footer_links } = (article.frontmatter as any)?.design ?? {};
23
+
24
+ // take binder url from article frontmatter or fallback to project
25
+ const binderUrl = article.frontmatter.binder ?? computeOptions.binderBadgeUrl;
26
+
21
27
  return (
22
28
  <ReferencesProvider
23
29
  references={{ ...article.references, article: article.mdast }}
24
30
  frontmatter={article.frontmatter}
25
31
  >
26
- <ThebeRenderMimeRegistryProvider>
27
- <ThebeSessionProvider start={false} name={article.slug}>
32
+ <BusyScopeProvider>
33
+ <ExecuteScopeProvider contents={article}>
28
34
  {!hide_title_block && (
29
35
  <FrontmatterBlock kind={article.kind} frontmatter={article.frontmatter} />
30
36
  )}
31
- <NotebookProvider siteConfig={false} page={article}>
32
- <div className="flex items-center">
33
- <div className="flex-grow"></div>
34
- {binder && <BinderBadge binder={binder} />}
35
- {canCompute && isJupyter && (
36
- <EnableCompute canCompute={true} key={article.slug}>
37
- <NotebookRunAll />
38
- </EnableCompute>
39
- )}
37
+ {binderUrl && !canCompute && (
38
+ <div className="flex justify-end">
39
+ <BinderBadge binder={binderUrl} />
40
40
  </div>
41
- <ContentBlocks pageKind={article.kind} mdast={article.mdast as GenericParent} />
42
- <Bibliography />
43
- {!hide_footer_links && <FooterLinksBlock links={article.footer} />}
44
- </NotebookProvider>
45
- </ThebeSessionProvider>
46
- </ThebeRenderMimeRegistryProvider>
41
+ )}
42
+ {canCompute && article.kind === SourceFileKind.Notebook && <NotebookToolbar showLaunch />}
43
+ {canCompute && article.kind === SourceFileKind.Article && <NotebookToolbar />}
44
+ <ContentBlocks pageKind={article.kind} mdast={article.mdast as GenericParent} />
45
+ <Bibliography />
46
+ <ConnectionStatusTray />
47
+ {!hide_footer_links && <FooterLinksBlock links={article.footer} />}
48
+ </ExecuteScopeProvider>
49
+ </BusyScopeProvider>
47
50
  </ReferencesProvider>
48
51
  );
49
52
  });
@@ -63,7 +63,7 @@ export function Document({
63
63
  <body className="m-0 transition-colors duration-500 bg-white dark:bg-stone-900">
64
64
  <ThemeProvider theme={theme} renderers={renderers} {...links}>
65
65
  <BaseUrlProvider baseurl={baseurl}>
66
- <ThebeBundleLoaderProvider loadThebeLite>
66
+ <ThebeBundleLoaderProvider loadThebeLite publicPath={baseurl}>
67
67
  <SiteProvider config={config}>
68
68
  <ConfiguredThebeServerProvider>{children}</ConfiguredThebeServerProvider>
69
69
  </SiteProvider>
@@ -1,131 +0,0 @@
1
- import { Spinner } from './Spinner';
2
- import PlayCircleIcon from '@heroicons/react/24/outline/PlayCircleIcon';
3
- import ArrowPathIcon from '@heroicons/react/24/outline/ArrowPathIcon';
4
- import MinusCircleIcon from '@heroicons/react/24/outline/MinusCircleIcon';
5
- import ArrowTopRightOnSquareIcon from '@heroicons/react/24/outline/ArrowTopRightOnSquareIcon';
6
- import classNames from 'classnames';
7
- import type { NotebookExecuteOptions } from 'thebe-react';
8
- import { useThebeServer } from 'thebe-react';
9
- import type { IThebeCellExecuteReturn } from 'thebe-core';
10
- import { useMDASTNotebook, useNotebookCellExecution } from '@myst-theme/jupyter';
11
-
12
- export function Run({
13
- ready,
14
- executing,
15
- disabled,
16
- execute,
17
- }: {
18
- ready: boolean;
19
- executing: boolean;
20
- disabled?: boolean;
21
- execute: (
22
- options?: NotebookExecuteOptions | undefined,
23
- ) => Promise<(IThebeCellExecuteReturn | null)[]>;
24
- }) {
25
- return (
26
- <div className="flex relative text-sm">
27
- <button
28
- className={classNames(
29
- 'cursor-pointer text-gray-700 active:text-green-700 hover:opacity-100',
30
- {
31
- 'opacity-10 hover:opacity-10': executing,
32
- 'opacity-60': !executing,
33
- },
34
- )}
35
- disabled={disabled || !ready || executing}
36
- onClick={() => execute()}
37
- >
38
- <PlayCircleIcon className="h-6 w-6 inline-block align-top" title="run all cells" />
39
- </button>
40
- {executing && (
41
- <span className="absolute top-0 left-0 z-10 w-[22px] h-[22px] opacity-100">
42
- <Spinner size={24} />
43
- </span>
44
- )}
45
- </div>
46
- );
47
- }
48
-
49
- export function Clear({
50
- ready,
51
- executing,
52
- disabled,
53
- clear,
54
- }: {
55
- ready: boolean;
56
- executing: boolean;
57
- disabled?: boolean;
58
- clear: () => void;
59
- }) {
60
- return (
61
- <button
62
- className="flex cursor-pointer text-gray-700 active:text-green-700 opacity-60 hover:opacity-100"
63
- disabled={disabled || !ready || executing}
64
- onClick={() => clear()}
65
- >
66
- <MinusCircleIcon className="h-6 w-6 inline-block align-top" title="clear all outputs" />
67
- </button>
68
- );
69
- }
70
-
71
- export function RunCell({ id }: { id: string }) {
72
- const exec = useNotebookCellExecution(id);
73
- if (!exec?.ready) return null;
74
- const { ready, executing, notebookIsExecuting, execute } = exec;
75
- return (
76
- <Run ready={ready} executing={executing} disabled={notebookIsExecuting} execute={execute} />
77
- );
78
- }
79
-
80
- export function ClearCell({ id }: { id: string }) {
81
- const exec = useNotebookCellExecution(id);
82
- if (!exec?.ready) return null;
83
- const { ready, executing, notebookIsExecuting, clear } = exec;
84
- return <Clear ready={ready} executing={executing} disabled={notebookIsExecuting} clear={clear} />;
85
- }
86
-
87
- export function NotebookRunAll() {
88
- const { ready: serverReady, server } = useThebeServer();
89
- const exec = useMDASTNotebook();
90
-
91
- if (!exec?.ready) return null;
92
- const { ready, executing, executeAll, restart, clear } = exec;
93
-
94
- const clickLaunchInJupyter = () => {
95
- if (!serverReady || !server?.settings) return;
96
- window.open(server.settings.baseUrl, '_blank');
97
- };
98
-
99
- return (
100
- <div className="flex">
101
- <div className="group flex relative space-x-1">
102
- <Run
103
- ready={ready}
104
- executing={executing}
105
- execute={(options) => {
106
- clear();
107
- return executeAll(options);
108
- }}
109
- />
110
- <button
111
- className="flex items-center cursor-pointer text-gray-700 active:text-green-700 opacity-60 hover:opacity-100"
112
- disabled={!ready || executing}
113
- onClick={() => restart()}
114
- >
115
- <ArrowPathIcon className="h-6 w-6" title="restart kernel" />
116
- </button>
117
- <Clear ready={ready} executing={executing} clear={clear} />
118
- <button
119
- className="flex items-center cursor-pointer text-gray-700 active:text-green-700 opacity-60 hover:opacity-100"
120
- disabled={!ready}
121
- onClick={clickLaunchInJupyter}
122
- >
123
- <ArrowTopRightOnSquareIcon
124
- className="h-6 w-6 inline-block align-top"
125
- title="launch in juptyer"
126
- />
127
- </button>
128
- </div>
129
- </div>
130
- );
131
- }
@@ -1,64 +0,0 @@
1
- import { useThebeLoader, useThebeServer, useThebeSession } from 'thebe-react';
2
- import PowerIcon from '@heroicons/react/24/outline/PowerIcon';
3
- import { useHasNotebookProvider } from '@myst-theme/jupyter';
4
- import { useNavigation } from '@remix-run/react';
5
- import { useEffect, useState } from 'react';
6
-
7
- export function EnableCompute({
8
- canCompute,
9
- children,
10
- }: React.PropsWithChildren<{ canCompute: boolean }>) {
11
- const { load, loading, core } = useThebeLoader();
12
- const { connect, connecting, ready: serverReady, error: serverError } = useThebeServer();
13
- const {
14
- start,
15
- starting,
16
- shutdown,
17
- session,
18
- ready: sessionReady,
19
- error: sessionError,
20
- } = useThebeSession();
21
- const hasNotebookProvider = useHasNotebookProvider();
22
- const navigation = useNavigation();
23
- const [enabling, setEnabling] = useState(false);
24
- const [enabled, setEnabled] = useState(false);
25
- const busy = enabling || loading || connecting || starting;
26
-
27
- useEffect(() => {
28
- if (!enabling) return;
29
- if (!core) return load();
30
- if (!serverReady) return connect();
31
- if (!sessionReady) start();
32
- if (sessionReady) {
33
- setEnabled(true);
34
- setEnabling(false);
35
- }
36
- }, [enabling, core, serverReady, sessionReady]);
37
-
38
- if (!canCompute || !hasNotebookProvider) return null;
39
- let classes = 'flex text-center mr-1 cursor-pointer rounded-full';
40
- const idleClasses = 'text-blue-700 hover:opacity-100 opacity-60';
41
- const busyClasses = 'bg-yellow-700 text-yellow-700 opacity-100 font-semibold';
42
- const readyClasses = 'bg-green-700 text-green-700 opacity-100 font-semibold';
43
- const errorClasses = 'bg-red-700 text-red-700 opacity-100';
44
-
45
- if (busy) classes += busyClasses;
46
- else if (serverReady && sessionReady) classes += readyClasses;
47
- else if (serverError || sessionError) classes += errorClasses;
48
- else classes += idleClasses;
49
-
50
- useEffect(() => {
51
- if (navigation.state === 'loading') {
52
- shutdown();
53
- }
54
- }, [shutdown, navigation]);
55
-
56
- return (
57
- <div className="flex mx-1 items-center mb-2">
58
- <button className={classes} onClick={() => setEnabling(true)} disabled={enabling || enabled}>
59
- <PowerIcon className="h-6 w-6 mx-1 inline-block align-top" title="enable compute" />
60
- </button>
61
- {enabled && <>{children}</>}
62
- </div>
63
- );
64
- }