diff --git a/.changeset/great-weeks-sit.md b/.changeset/great-weeks-sit.md new file mode 100644 index 000000000..0da955fb7 --- /dev/null +++ b/.changeset/great-weeks-sit.md @@ -0,0 +1,5 @@ +--- +'@myst-theme/site': patch +--- + +Mark ArticlePage as deprecated diff --git a/.changeset/healthy-waves-sell.md b/.changeset/healthy-waves-sell.md new file mode 100644 index 000000000..cd1feefaf --- /dev/null +++ b/.changeset/healthy-waves-sell.md @@ -0,0 +1,6 @@ +--- +'@myst-theme/article': patch +'@myst-theme/book': patch +--- + +Support inline navigation diff --git a/packages/site/src/pages/Article.tsx b/packages/site/src/pages/Article.tsx index 0c3e49228..94be713c5 100644 --- a/packages/site/src/pages/Article.tsx +++ b/packages/site/src/pages/Article.tsx @@ -41,6 +41,11 @@ function combineDownloads( return pageFrontmatter.exports; } +/** + * @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, diff --git a/themes/article/app/components/Article.tsx b/themes/article/app/components/Article.tsx index 2c35f250b..853b99c8b 100644 --- a/themes/article/app/components/Article.tsx +++ b/themes/article/app/components/Article.tsx @@ -21,11 +21,13 @@ export function Article({ hideKeywords, hideOutline, hideTitle, + outlineMaxDepth, }: { article: PageLoader; hideKeywords?: boolean; hideOutline?: boolean; hideTitle?: boolean; + outlineMaxDepth?: number; }) { const keywords = article.frontmatter?.keywords ?? []; const tree = copyNode(article.mdast); @@ -42,12 +44,13 @@ export function Article({ {!hideTitle && } {!hideOutline && ( -
- +
+
)} + {compute?.enabled && compute?.features.notebookCompute && article.kind === SourceFileKind.Notebook && } diff --git a/themes/article/app/components/ArticlePage.tsx b/themes/article/app/components/ArticlePage.tsx index 617f51aff..cb1ceab68 100644 --- a/themes/article/app/components/ArticlePage.tsx +++ b/themes/article/app/components/ArticlePage.tsx @@ -1,5 +1,9 @@ import type { PageLoader } from '@myst-theme/common'; -import { FooterLinksBlock, ArticleHeader, Error404 } from '@myst-theme/site'; +import { + FooterLinksBlock, + ArticleHeader, + Error404, +} from '@myst-theme/site'; import { LaunchBinder, useComputeOptions } from '@myst-theme/jupyter'; import { ArrowLeftIcon } from '@heroicons/react/24/outline'; import { DocumentArrowDownIcon } from '@heroicons/react/24/outline'; @@ -15,17 +19,20 @@ import classNames from 'classnames'; import { BusyScopeProvider, ExecuteScopeProvider } from '@myst-theme/jupyter'; import { DownloadLinksArea } from './Downloads'; import { Article } from './Article'; - -export interface ArticleTemplateOptions { - hide_toc?: boolean; - hide_footer_links?: boolean; - numbered_references?: boolean; -} +import type { TemplateOptions } from '../types.js'; export function ArticlePage({ article }: { article: PageLoader }) { const grid = useGridSystemProvider(); - const { projects, hide_footer_links } = (useSiteManifest() ?? {}) as SiteManifest & - ArticleTemplateOptions; + + const siteManifest = useSiteManifest() as SiteManifest; + const pageDesign: TemplateOptions = (article.frontmatter as any)?.options ?? {}; + const siteDesign: TemplateOptions = siteManifest?.options ?? {}; + + const { projects } = siteManifest; + const { hide_footer_links, hide_outline, outline_maxdepth } = { + ...siteDesign, + ...pageDesign, + }; const Link = useLinkProvider(); const baseurl = useBaseurl(); const compute = useComputeOptions(); @@ -91,7 +98,13 @@ export function ArticlePage({ article }: { article: PageLoader }) {
)} -
+
{!hide_footer_links && } @@ -99,3 +112,4 @@ export function ArticlePage({ article }: { article: PageLoader }) { ); } + diff --git a/themes/article/app/routes/_index.tsx b/themes/article/app/routes/_index.tsx index 2c5446466..a1f67919c 100644 --- a/themes/article/app/routes/_index.tsx +++ b/themes/article/app/routes/_index.tsx @@ -1,9 +1,4 @@ -import { - ProjectPageCatchBoundary, - getMetaTagsForArticle, - responseNoArticle, - responseNoSite, -} from '@myst-theme/site'; +import { getMetaTagsForArticle, responseNoArticle, responseNoSite, ProjectPageCatchBoundary} from '@myst-theme/site'; import Page from './$'; import { ArticlePageAndNavigation } from '../components/ArticlePageAndNavigation'; import { getConfig, getPage } from '../utils/loaders.server'; diff --git a/themes/article/app/types.ts b/themes/article/app/types.ts new file mode 100644 index 000000000..1416b4172 --- /dev/null +++ b/themes/article/app/types.ts @@ -0,0 +1,6 @@ +export interface TemplateOptions { + hide_toc?: boolean; + hide_outline?: boolean; + hide_footer_links?: boolean; + outline_maxdepth?: number; +} diff --git a/themes/article/template.yml b/themes/article/template.yml index d25473c64..d53d5e2dd 100644 --- a/themes/article/template.yml +++ b/themes/article/template.yml @@ -19,6 +19,15 @@ options: - type: boolean id: hide_footer_links description: Hide the previous/next links in the footer + - type: boolean + id: hide_outline + description: Hide the document outline on all pages + - type: number + id: outline_maxdepth + description: The maximum depth to show on the document outline, for example, `2` would show only two depths of headings (e.g. `

` and `

`). + min: 1 + max: 6 + integer: true - type: string id: twitter description: Twitter handle related to the site diff --git a/themes/book/app/components/ArticlePage.tsx b/themes/book/app/components/ArticlePage.tsx new file mode 100644 index 000000000..755a21abb --- /dev/null +++ b/themes/book/app/components/ArticlePage.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { ReferencesProvider, useProjectManifest, useSiteManifest } from '@myst-theme/providers'; +import { + Bibliography, + ContentBlocks, + FooterLinksBlock, + FrontmatterParts, + BackmatterParts, + DocumentOutline, + extractKnownParts, +} from '@myst-theme/site'; +import type { SiteManifest } from 'myst-config'; +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 type { SiteAction } from 'myst-config'; +import type { TemplateOptions } from '../types.js'; + +/** + * Combines the project downloads and the export options + */ +function combineDownloads( + siteDownloads: SiteAction[] | undefined, + pageFrontmatter: PageLoader['frontmatter'], +) { + if (pageFrontmatter.downloads) { + return pageFrontmatter.downloads; + } + // No downloads on the page, combine the exports if they exist + if (siteDownloads) { + return [...(pageFrontmatter.exports ?? []), ...siteDownloads]; + } + return pageFrontmatter.exports; +} + +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 pageDesign: TemplateOptions = (article.frontmatter as any)?.options ?? {}; + const siteDesign: TemplateOptions = + (useSiteManifest() as SiteManifest & TemplateOptions)?.options ?? {}; + const { hide_title_block, hide_footer_links, hide_outline, outline_maxdepth } = { + ...siteDesign, + ...pageDesign, + }; + const downloads = combineDownloads(manifest?.downloads, article.frontmatter); + const tree = copyNode(article.mdast); + const keywords = article.frontmatter?.keywords ?? []; + const parts = extractKnownParts(tree); + + return ( + + + + {!hide_title_block && ( + + )} + {!hide_outline && ( +
+ +
+ )} + {compute?.enabled && + compute.features.notebookCompute && + article.kind === SourceFileKind.Notebook && } + {compute?.enabled && article.kind === SourceFileKind.Article && ( + + )} +