diff --git a/.changeset/cool-onions-admire.md b/.changeset/cool-onions-admire.md new file mode 100644 index 00000000..315bcf70 --- /dev/null +++ b/.changeset/cool-onions-admire.md @@ -0,0 +1,5 @@ +--- +'@myst-theme/site': patch +--- + +Remove unused function `isACodeCell` diff --git a/.changeset/famous-cows-sleep.md b/.changeset/famous-cows-sleep.md new file mode 100644 index 00000000..e372fc8c --- /dev/null +++ b/.changeset/famous-cows-sleep.md @@ -0,0 +1,5 @@ +--- +'@myst-theme/site': patch +--- + +Remove deprecated `ArticlePage` diff --git a/.changeset/fuzzy-plums-accept.md b/.changeset/fuzzy-plums-accept.md new file mode 100644 index 00000000..8404a3ae --- /dev/null +++ b/.changeset/fuzzy-plums-accept.md @@ -0,0 +1,5 @@ +--- +'@myst-theme/site': patch +--- + +Remove dependence on `@myst-theme/jupyter` diff --git a/.changeset/honest-sheep-compare.md b/.changeset/honest-sheep-compare.md new file mode 100644 index 00000000..9f357b1d --- /dev/null +++ b/.changeset/honest-sheep-compare.md @@ -0,0 +1,11 @@ +--- +'@myst-theme/site': minor +'myst-to-react': patch +'myst-demo': patch +'@myst-theme/providers': patch +'@myst-theme/diagrams': patch +'@myst-theme/article': patch +'@myst-theme/book': patch +--- + +Move ContentBlocks to MyST diff --git a/.changeset/red-lies-taste.md b/.changeset/red-lies-taste.md new file mode 100644 index 00000000..2338e502 --- /dev/null +++ b/.changeset/red-lies-taste.md @@ -0,0 +1,5 @@ +--- +'@myst-theme/providers': patch +--- + +Move `ReferencesProvider` to `ArticleProvider` diff --git a/package-lock.json b/package-lock.json index c34c57fa..def2a4c8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -41492,7 +41492,6 @@ "@myst-theme/common": "^0.13.7", "@myst-theme/diagrams": "^0.13.7", "@myst-theme/frontmatter": "^0.13.7", - "@myst-theme/jupyter": "^0.13.7", "@myst-theme/providers": "^0.13.7", "@myst-theme/search": "^0.13.7", "@radix-ui/react-collapsible": "^1.0.3", diff --git a/packages/jupyter/src/block.tsx b/packages/jupyter/src/block.tsx new file mode 100644 index 00000000..48c85a4b --- /dev/null +++ b/packages/jupyter/src/block.tsx @@ -0,0 +1,88 @@ +import { Details, MyST } from 'myst-to-react'; +import { SourceFileKind } from 'myst-spec-ext'; +import type { GenericParent } from 'myst-common'; +import classNames from 'classnames'; +import { + NotebookClearCell, + NotebookRunCell, + NotebookRunCellSpinnerOnly, +} from './controls/index.js'; +import { useGridSystemProvider, usePageKind } from '@myst-theme/providers'; +import type { NodeRenderers, NodeRenderer } from '@myst-theme/providers'; + +export function NotebookBlock({ + id, + node, + className, +}: { + id: string; + node: GenericParent; + className?: string; +}) { + const pageKind = usePageKind(); + const grid = useGridSystemProvider(); + const subGrid = node.visibility === 'hide' ? '' : `${grid} subgrid-gap col-screen`; + const dataClassName = typeof node.data?.class === 'string' ? node.data?.class : undefined; + // Hide the subgrid if either the dataClass or the className exists and includes `col-` + const noSubGrid = + (dataClassName && dataClassName.includes('col-')) || (className && className.includes('col-')); + const block = ( +
+ {pageKind === SourceFileKind.Notebook && node.kind === 'notebook-code' && ( + <> +
+
+ +
+
+
+
+ + +
+
+ + )} + +
+ ); + if (node.visibility === 'hide') { + return
{block}
; + } + return block; +} + +export const NotebookBlockRenderer: NodeRenderer = ({ node, className }) => { + return ( + + ); +}; + +/** + * The logic for the selector is complex: + * + * - MD files have `notebook-code` and otherwise blocks with no kind. + * - IPyNB files have both `notebook-code` and `notebook-content` blocks. + * - An article may contain `notebook-code` blocks from the `code-cell` directive even though it is not considered a notebook (i.e., no frontmatter kernelspec). + * + * This component therefore only renders for `code-cells` (both ipynb and .md) and `content-cells` (ipynb). + * + * The benefit of this is that we can use the block-kind to figure out if e.g. the cell is executable. + */ +export const NOTEBOOK_BLOCK_RENDERERS: NodeRenderers = { + block: { + 'block[kind=notebook-code],block[kind=notebook-content]': NotebookBlockRenderer, + }, +}; diff --git a/packages/jupyter/src/index.tsx b/packages/jupyter/src/index.tsx index 9a6a6857..d7e14131 100644 --- a/packages/jupyter/src/index.tsx +++ b/packages/jupyter/src/index.tsx @@ -1,13 +1,3 @@ -import { Embed } from './embed.js'; -import { Output } from './output.js'; -import { Figure } from './figure.js'; - -const OUTPUT_RENDERERS = { - output: Output, - embed: Embed, - container: Figure, -}; - export * from './BinderBadge.js'; export * from './ErrorTray.js'; export * from './ConnectionStatusTray.js'; @@ -16,5 +6,4 @@ export * from './execute/index.js'; export * from './controls/index.js'; export * from './utils.js'; export { useLaunchBinder } from './hooks.js'; - -export default OUTPUT_RENDERERS; +export { JUPYTER_RENDERERS, NOTEBOOK_BLOCK_RENDERERS, OUTPUT_RENDERERS } from './renderers.js'; diff --git a/packages/jupyter/src/renderers.ts b/packages/jupyter/src/renderers.ts new file mode 100644 index 00000000..b0326182 --- /dev/null +++ b/packages/jupyter/src/renderers.ts @@ -0,0 +1,13 @@ +import { Embed } from './embed.js'; +import { Output } from './output.js'; +import { Figure } from './figure.js'; +import { mergeRenderers } from '@myst-theme/providers'; +import { NOTEBOOK_BLOCK_RENDERERS } from './block.js'; + +export { NOTEBOOK_BLOCK_RENDERERS } from './block.js'; +export const OUTPUT_RENDERERS = { + output: Output, + embed: Embed, + container: Figure, +}; +export const JUPYTER_RENDERERS = mergeRenderers([OUTPUT_RENDERERS, NOTEBOOK_BLOCK_RENDERERS]); diff --git a/packages/myst-demo/src/index.tsx b/packages/myst-demo/src/index.tsx index 509a47a3..b72e3aec 100644 --- a/packages/myst-demo/src/index.tsx +++ b/packages/myst-demo/src/index.tsx @@ -17,7 +17,7 @@ import type { DocxResult } from 'myst-to-docx'; import { validatePageFrontmatter } from 'myst-frontmatter'; import type { PageFrontmatter } from 'myst-frontmatter'; import type { NodeRenderer } from '@myst-theme/providers'; -import { ReferencesProvider } from '@myst-theme/providers'; +import { ArticleProvider, GridSystemProvider } from '@myst-theme/providers'; import { CopyIcon, CodeBlock, MyST } from 'myst-to-react'; import { useEffect, useRef, useState } from 'react'; import classnames from 'classnames'; @@ -445,10 +445,16 @@ export function MySTRenderer({ > {previewType === 'DEMO' && ( <> - - {TitleBlock && } - - + + + {TitleBlock && } + + + )} {previewType === 'AST' && ( diff --git a/packages/myst-to-react/src/block.tsx b/packages/myst-to-react/src/block.tsx new file mode 100644 index 00000000..adbc8e01 --- /dev/null +++ b/packages/myst-to-react/src/block.tsx @@ -0,0 +1,51 @@ +import { Details } from './dropdown.js'; +import { MyST } from './MyST.js'; +import type { GenericParent } from 'myst-common'; +import classNames from 'classnames'; +import { useGridSystemProvider } from '@myst-theme/providers'; +import type { NodeRenderer } from '@myst-theme/providers'; + +export function Block({ + id, + node, + className, +}: { + id: string; + node: GenericParent; + className?: string; +}) { + const grid = useGridSystemProvider(); + const subGrid = node.visibility === 'hide' ? '' : `${grid} subgrid-gap col-screen`; + const dataClassName = typeof node.data?.class === 'string' ? node.data?.class : undefined; + // Hide the subgrid if either the dataClass or the className exists and includes `col-` + const noSubGrid = + (dataClassName && dataClassName.includes('col-')) || (className && className.includes('col-')); + const block = ( +
+ +
+ ); + if (node.visibility === 'hide') { + return
{block}
; + } + return block; +} + +export const BlockRenderer: NodeRenderer = ({ node, className }) => { + return ( + + ); +}; + +const BLOCK_RENDERERS: Record = { + block: BlockRenderer, +}; + +export default BLOCK_RENDERERS; diff --git a/packages/myst-to-react/src/index.tsx b/packages/myst-to-react/src/index.tsx index 71e88fcd..00233897 100644 --- a/packages/myst-to-react/src/index.tsx +++ b/packages/myst-to-react/src/index.tsx @@ -2,6 +2,7 @@ import { mergeRenderers } from '@myst-theme/providers'; import BASIC_RENDERERS from './basic.js'; import ADMONITION_RENDERERS from './admonitions.js'; import DROPDOWN_RENDERERS from './dropdown.js'; +import BLOCK_RENDERERS from './block.js'; import CARD_RENDERERS from './card.js'; import GRID_RENDERERS from './grid.js'; import CITE_RENDERERS from './cite.js'; @@ -22,6 +23,7 @@ import EXERCISE_RENDERERS from './exercise.js'; import ASIDE_RENDERERS from './aside.js'; import UNKNOWN_MYST_RENDERERS from './unknown.js'; +export { Block } from './block.js'; export { CopyIcon, HoverPopover, Tooltip, LinkCard } from './components/index.js'; export { CodeBlock } from './code.js'; export { HashLink, scrollToElement } from './hashLink.js'; @@ -34,6 +36,7 @@ export const DEFAULT_RENDERERS = mergeRenderers( [ BASIC_RENDERERS, UNKNOWN_MYST_RENDERERS, + BLOCK_RENDERERS, IMAGE_RENDERERS, LINK_RENDERERS, CODE_RENDERERS, diff --git a/packages/providers/src/article.tsx b/packages/providers/src/article.tsx new file mode 100644 index 00000000..91f99d81 --- /dev/null +++ b/packages/providers/src/article.tsx @@ -0,0 +1,62 @@ +import React, { useContext } from 'react'; +import type { References } from 'myst-common'; +import type { PageLoader } from '@myst-theme/common'; +import { SourceFileKind } from 'myst-spec-ext'; + +const ArticleContext = React.createContext<{ + kind?: SourceFileKind; + frontmatter?: PageLoader['frontmatter']; + references?: References; +}>({}); + +export function ArticleProvider({ + kind, + references, + frontmatter, + children, +}: { + kind: SourceFileKind; + frontmatter?: PageLoader['frontmatter']; + references?: References; + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} + +export function useReferences() { + const data = useContext(ArticleContext); + return data?.references; +} + +export function useFrontmatter() { + const data = useContext(ArticleContext); + return data?.frontmatter; +} + +export function usePageKind() { + const data = useContext(ArticleContext); + return data?.kind ?? SourceFileKind.Article; +} + +/** + * @deprecated This component is not maintained, in favor of the reworked `ArticleProvider` component + */ +export function ReferencesProvider({ + references, + frontmatter, + children, +}: { + frontmatter?: PageLoader['frontmatter']; + references?: References; + children: React.ReactNode; +}) { + return ( + + {children} + + ); +} diff --git a/packages/providers/src/index.tsx b/packages/providers/src/index.tsx index 2b5564e5..d7e762f4 100644 --- a/packages/providers/src/index.tsx +++ b/packages/providers/src/index.tsx @@ -1,7 +1,7 @@ export * from './hooks.js'; export * from './theme.js'; export * from './grid.js'; -export * from './references.js'; +export * from './article.js'; export * from './baseurl.js'; export * from './ui.js'; export * from './site.js'; diff --git a/packages/providers/src/references.tsx b/packages/providers/src/references.tsx deleted file mode 100644 index 392ce488..00000000 --- a/packages/providers/src/references.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import React, { useContext } from 'react'; -import type { References } from 'myst-common'; -import type { PageLoader } from '@myst-theme/common'; - -const ReferencesContext = React.createContext<{ - frontmatter?: PageLoader['frontmatter']; - references?: References; -}>({}); - -export function ReferencesProvider({ - references, - frontmatter, - children, -}: { - frontmatter?: PageLoader['frontmatter']; - references?: References; - children: React.ReactNode; -}) { - return ( - - {children} - - ); -} - -export function useReferences() { - const data = useContext(ReferencesContext); - return data?.references; -} - -export function useFrontmatter() { - const data = useContext(ReferencesContext); - return data?.frontmatter; -} diff --git a/packages/providers/src/renderers.tsx b/packages/providers/src/renderers.tsx index e944b5b2..8d2307a5 100644 --- a/packages/providers/src/renderers.tsx +++ b/packages/providers/src/renderers.tsx @@ -31,6 +31,15 @@ export function validateRenderers(renderers?: NodeRenderers): NodeRenderersValid return validatedRenderers; } +/** + * Combines a list of renderers. Put more specific renderers **later** in the list. + * + * When a renderer is selected, it will look for a match in **reversed** order. + * + * ```typescript + * mergeRenderers([defaultRenderers, specificRenderers]) + * ``` + */ export function mergeRenderers(renderers: NodeRenderers[], validate: true): NodeRenderersValidated; export function mergeRenderers(renderers: NodeRenderers[], validate?: false): NodeRenderers; export function mergeRenderers(renderers: NodeRenderers[], validate?: boolean): NodeRenderers { diff --git a/packages/site/package.json b/packages/site/package.json index 1ef29a58..50c1d486 100644 --- a/packages/site/package.json +++ b/packages/site/package.json @@ -24,7 +24,6 @@ "@myst-theme/common": "^0.13.7", "@myst-theme/diagrams": "^0.13.7", "@myst-theme/frontmatter": "^0.13.7", - "@myst-theme/jupyter": "^0.13.7", "@myst-theme/providers": "^0.13.7", "@myst-theme/search": "^0.13.7", "@radix-ui/react-collapsible": "^1.0.3", diff --git a/packages/site/src/components/Abstract.tsx b/packages/site/src/components/Abstract.tsx index b644257f..a2e2e89e 100644 --- a/packages/site/src/components/Abstract.tsx +++ b/packages/site/src/components/Abstract.tsx @@ -1,6 +1,5 @@ import type { GenericParent } from 'myst-common'; -import { ContentBlocks } from './ContentBlocks.js'; -import { HashLink } from 'myst-to-react'; +import { MyST, HashLink } from 'myst-to-react'; export function Abstract({ content, @@ -21,7 +20,7 @@ export function Abstract({
- +
); diff --git a/packages/site/src/components/ContentBlocks.tsx b/packages/site/src/components/ContentBlocks.tsx index 8ab1462e..33553629 100644 --- a/packages/site/src/components/ContentBlocks.tsx +++ b/packages/site/src/components/ContentBlocks.tsx @@ -1,89 +1,20 @@ -import { Details, MyST } from 'myst-to-react'; -import { SourceFileKind } from 'myst-spec-ext'; +import type { SourceFileKind } from 'myst-spec-ext'; import type { GenericParent } from 'myst-common'; -import classNames from 'classnames'; -import { - NotebookClearCell, - NotebookRunCell, - NotebookRunCellSpinnerOnly, -} from '@myst-theme/jupyter'; -import { useGridSystemProvider } from '@myst-theme/providers'; -import { isACodeCell } from '../utils.js'; - -export function Block({ - id, - pageKind, - node, - className, -}: { - id: string; - pageKind: SourceFileKind; - node: GenericParent; - className?: string; -}) { - const grid = useGridSystemProvider(); - const subGrid = node.visibility === 'hide' ? '' : `${grid} subgrid-gap col-screen`; - const dataClassName = typeof node.data?.class === 'string' ? node.data?.class : undefined; - // Hide the subgrid if either the dataClass or the className exists and includes `col-` - const noSubGrid = - (dataClassName && dataClassName.includes('col-')) || (className && className.includes('col-')); - const block = ( -
- {pageKind === SourceFileKind.Notebook && isACodeCell(node) && ( - <> -
-
- -
-
-
-
- - -
-
- - )} - -
- ); - if (node.visibility === 'hide') { - return
{block}
; - } - return block; -} +import { MyST, Block as MystBlock } from 'myst-to-react'; +/** + * @deprecated This component is not maintained, in favor of the generic `MyST` component + */ export function ContentBlocks({ mdast, - pageKind = SourceFileKind.Article, className, }: { mdast: GenericParent; pageKind?: SourceFileKind; className?: string; }) { - if (!mdast) return null; - const blocks = mdast.children as GenericParent[]; - return ( - <> - {blocks - .filter((node) => node.visibility !== 'remove') - .map((node) => ( - - ))} - - ); + return ; } + +/** @deprecated use `import { Block } from 'myst-to-react';` */ +export const Block = MystBlock; diff --git a/packages/site/src/components/renderers.ts b/packages/site/src/components/renderers.ts index ef49c3b6..d2f2394d 100644 --- a/packages/site/src/components/renderers.ts +++ b/packages/site/src/components/renderers.ts @@ -2,11 +2,9 @@ import type { NodeRenderers } from '@myst-theme/providers'; import { DEFAULT_RENDERERS } from 'myst-to-react'; import { MystDemoRenderer } from 'myst-demo'; import { MermaidNodeRenderer } from '@myst-theme/diagrams'; -import OUTPUT_RENDERERS from '@myst-theme/jupyter'; export const renderers: NodeRenderers = { ...DEFAULT_RENDERERS, myst: MystDemoRenderer, mermaid: MermaidNodeRenderer, - ...OUTPUT_RENDERERS, }; diff --git a/packages/site/src/pages/Article.tsx b/packages/site/src/pages/Article.tsx deleted file mode 100644 index 0b5e0439..00000000 --- a/packages/site/src/pages/Article.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import React from 'react'; -import { ReferencesProvider, useProjectManifest } from '@myst-theme/providers'; -import { - Bibliography, - ContentBlocks, - FooterLinksBlock, - FrontmatterParts, - BackmatterParts, -} from '../components/index.js'; -import type { PageLoader } from '@myst-theme/common'; -import { copyNode, type GenericParent } from 'myst-common'; -import { SourceFileKind } from 'myst-spec-ext'; -import { - ExecuteScopeProvider, - BusyScopeProvider, - NotebookToolbar, - ConnectionStatusTray, - ErrorTray, - useComputeOptions, -} from '@myst-theme/jupyter'; -import { FrontmatterBlock } from '@myst-theme/frontmatter'; -import { combineDownloads, extractKnownParts } from '../utils.js'; - -/** - * @deprecated This component is not maintained, in favor of theme-specific ArticlePages - * - * As examples, MyST book and article themes define their own ArticlePage components. - */ -export const ArticlePage = React.memo(function ({ - article, - hide_all_footer_links, - hideKeywords, -}: { - article: PageLoader; - hide_all_footer_links?: boolean; - hideKeywords?: boolean; -}) { - const manifest = useProjectManifest(); - const compute = useComputeOptions(); - - const { hide_title_block, hide_footer_links } = (article.frontmatter as any)?.options ?? {}; - const downloads = combineDownloads(manifest?.downloads, article.frontmatter); - const tree = copyNode(article.mdast); - const keywords = article.frontmatter?.keywords ?? []; - const parts = extractKnownParts(tree, article.frontmatter?.parts); - - const { thebe } = manifest as any; - const { location } = article; - - return ( - - - - {!hide_title_block && ( - - )} - {compute?.enabled && - compute.features.notebookCompute && - article.kind === SourceFileKind.Notebook && } - {compute?.enabled && article.kind === SourceFileKind.Article && ( - - )} -