@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 +8 -8
- package/src/components/Bibliography.tsx +3 -3
- package/src/components/ContentBlocks.tsx +21 -7
- package/src/components/DocumentOutline.tsx +2 -2
- package/src/components/FooterLinksBlock.tsx +5 -5
- package/src/components/Navigation/TableOfContents.tsx +3 -3
- package/src/components/Navigation/ThemeButton.tsx +1 -1
- package/src/components/Navigation/TopNav.tsx +51 -18
- package/src/loaders/utils.ts +2 -0
- package/src/pages/Article.tsx +28 -25
- package/src/pages/Root.tsx +1 -1
- package/src/components/ComputeControls.tsx +0 -131
- package/src/components/EnableCompute.tsx +0 -64
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@myst-theme/site",
|
|
3
|
-
"version": "0.3.
|
|
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.
|
|
20
|
-
"@myst-theme/frontmatter": "^0.3.
|
|
21
|
-
"@myst-theme/jupyter": "^0.3.
|
|
22
|
-
"@myst-theme/providers": "^0.3.
|
|
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.
|
|
28
|
-
"myst-to-react": "^0.3.
|
|
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.
|
|
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
|
|
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="
|
|
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
|
|
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 {
|
|
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
|
-
{
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
|
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="
|
|
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="
|
|
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
|
|
22
|
+
<div className="flex h-full align-middle">
|
|
23
23
|
{right && (
|
|
24
|
-
<ArrowLeftIcon className="w-6 h-6
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 = '
|
|
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
|
|
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
|
|
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
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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="
|
|
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({
|
|
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
|
|
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
|
|
197
|
-
|
|
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 } =
|
|
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="
|
|
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
|
|
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
|
|
265
|
+
<div className="flex items-center flex-grow w-auto">
|
|
233
266
|
<NavItems nav={nav} />
|
|
234
|
-
<div className="
|
|
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
|
|
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}
|
package/src/loaders/utils.ts
CHANGED
|
@@ -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
|
package/src/pages/Article.tsx
CHANGED
|
@@ -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 {
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
<
|
|
27
|
-
<
|
|
32
|
+
<BusyScopeProvider>
|
|
33
|
+
<ExecuteScopeProvider contents={article}>
|
|
28
34
|
{!hide_title_block && (
|
|
29
35
|
<FrontmatterBlock kind={article.kind} frontmatter={article.frontmatter} />
|
|
30
36
|
)}
|
|
31
|
-
|
|
32
|
-
<div className="flex
|
|
33
|
-
<
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
});
|
package/src/pages/Root.tsx
CHANGED
|
@@ -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
|
-
}
|