diff --git a/docs/make.jl b/docs/make.jl index 59ded9221..710318cab 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -11,6 +11,7 @@ using Documenter using DocumenterVitepress using DocumenterCitations using DocumenterInterLinks +include("overrides.jl") # examples example_dir = joinpath(@__DIR__, "src", "examples") @@ -80,7 +81,7 @@ makedocs(; ], checkdocs = :exports, doctest = true, - plugins = [bib, links] + plugins = [bib, links, VitepressOverrides()] ) DocumenterVitepress.deploydocs(; diff --git a/docs/overrides.jl b/docs/overrides.jl new file mode 100644 index 000000000..36e0afcce --- /dev/null +++ b/docs/overrides.jl @@ -0,0 +1,12 @@ +# Apply local overrides after DocumenterVitepress generates its upstream config. +struct VitepressOverrides <: Documenter.Plugin end + +function DocumenterVitepress.vitepress_config_transform(::VitepressOverrides, config::String) + marker = r"(?m)^export\s+default\s+" + occursin(marker, config) || error("DocumenterVitepress config has no default export to extend") + return replace(config, marker => "const upstreamConfig = "; count = 1) * """ + + import { withOverrides } from './overrides.mts' + export default withOverrides(upstreamConfig) + """ +end diff --git a/docs/src/.vitepress/overrides.mts b/docs/src/.vitepress/overrides.mts new file mode 100644 index 000000000..03970e439 --- /dev/null +++ b/docs/src/.vitepress/overrides.mts @@ -0,0 +1,44 @@ +// VitePress indexes headings, so give each docstring summary a heading in the +// search render only. The title must precede the anchor for its section indexer. +const DOCSTRING_SUMMARY = + /(.*?)<\/span><\/a>.*?<\/summary>/g + +// Documenter turns docstring headings into bold-only paragraphs. Mark paragraphs +// wrapped in one strong span; overrides.css scopes their styling to docstrings. +// Keeping the scope in CSS avoids tracking raw HTML (including nested details). +function docstringHeadings(md) { + md.core.ruler.push('mpskit_docstring_headings', ({ tokens }) => { + for (let i = 0; i < tokens.length; i++) { + if (tokens[i].type !== 'paragraph_open') continue + const children = tokens[i + 1]?.children?.filter( + (c) => !(c.type === 'text' && c.content === '') + ) + if (children?.[0]?.type !== 'strong_open') continue + // The matching close must end the paragraph, excluding text or another + // strong span after it while allowing emphasis and links inside the span. + const end = children.findIndex( + (c) => c.type === 'strong_close' && c.level === children[0].level + ) + if (end === children.length - 1) tokens[i].attrJoin('class', 'jldocstring-heading') + } + }) +} + +// Extend the generated config while retaining all upstream plugins and options. +export function withOverrides(config) { + const configureMarkdown = config.markdown.config + config.markdown.config = (md) => { + configureMarkdown(md) + md.use(docstringHeadings) + } + config.themeConfig.search.options._render = (src, env, md) => { + const html = md.render(src, env) + if (env.frontmatter?.search === false) return '' + return html.replace( + DOCSTRING_SUMMARY, + (_match, id, href, name) => + `

${name} ​

` + ) + } + return config +} diff --git a/docs/src/.vitepress/theme/custom.css b/docs/src/.vitepress/theme/custom.css deleted file mode 100644 index bcc238a95..000000000 --- a/docs/src/.vitepress/theme/custom.css +++ /dev/null @@ -1,21 +0,0 @@ -/* ===== MPSKit branding ===== */ -/* Accent color matching the (green) MPSKit logo in light mode. - Dark mode already uses the green palette defined by the DVP template. */ -:root { - --vp-c-brand: var(--julia-green); - --vp-c-brand-1: #389826; - --vp-c-brand-2: #2f8420; - --vp-c-brand-3: #277018; - --vp-c-brand-light: #389826; -} - -/* ===== Color-invertible diagrams ===== - The manual embeds line-drawing diagrams (black on transparent) via - `@raw html` . Legible on a light background - as-is; invert them in dark mode so they read as light-on-dark. */ -.color-invertible { - transition: filter 0.2s ease; -} -.dark .color-invertible { - filter: invert(1) hue-rotate(180deg); -} diff --git a/docs/src/.vitepress/theme/index.ts b/docs/src/.vitepress/theme/index.ts deleted file mode 100644 index ada1bbeae..000000000 --- a/docs/src/.vitepress/theme/index.ts +++ /dev/null @@ -1,44 +0,0 @@ -// .vitepress/theme/index.ts -import { h } from 'vue' -import DefaultTheme from 'vitepress/theme' -import type { Theme as ThemeConfig } from 'vitepress' -import 'virtual:mathjax-styles.css'; - -import { - NolebaseEnhancedReadabilitiesMenu, - NolebaseEnhancedReadabilitiesScreenMenu, -} from '@nolebase/vitepress-plugin-enhanced-readabilities/client' - -import VersionPicker from "@/VersionPicker.vue" -import AuthorBadge from '@/AuthorBadge.vue' -import Authors from '@/Authors.vue' -import SidebarDrawerToggle from '@/SidebarDrawerToggle.vue' - -import { enhanceAppWithTabs } from 'vitepress-plugin-tabs/client' - -import '@nolebase/vitepress-plugin-enhanced-readabilities/client/style.css' -import './style.css' // template default, auto-supplied to the build by DVP -import './docstrings.css' // template default, auto-supplied to the build by DVP -import './custom.css' // MPSKit customizations (this repo) - -export const Theme: ThemeConfig = { - extends: DefaultTheme, - Layout() { - return h(DefaultTheme.Layout, null, { - 'nav-bar-content-after': () => [ - h(NolebaseEnhancedReadabilitiesMenu), // Enhanced Readabilities menu - ], - // A enhanced readabilities menu for narrower screens (usually smaller than iPad Mini) - 'nav-screen-content-after': () => h(NolebaseEnhancedReadabilitiesScreenMenu), - // Sidebar drawer toggle button (to the left of search bar) - 'nav-bar-content-before': () => h(SidebarDrawerToggle), - }) - }, - enhanceApp({ app, router, siteData }) { - enhanceAppWithTabs(app); - app.component('VersionPicker', VersionPicker); - app.component('AuthorBadge', AuthorBadge) - app.component('Authors', Authors) - } -} -export default Theme diff --git a/docs/src/.vitepress/theme/overrides.css b/docs/src/.vitepress/theme/overrides.css new file mode 100644 index 000000000..8bde80adc --- /dev/null +++ b/docs/src/.vitepress/theme/overrides.css @@ -0,0 +1,80 @@ +/* Allow bold synthesis for the upstream font stack on Linux (#477). */ +body { + font-synthesis: weight style; +} + +/* Documenter flattens headings to bold paragraphs; overrides.mts marks them. */ +.jldocstring.custom-block p.jldocstring-heading { + margin: 1.5rem 0 0.6rem; + padding-bottom: 0.25rem; + font-size: 1em; + font-weight: 700; + line-height: 1.4; + color: var(--vp-c-text-1); + border-bottom: 1px solid var(--vp-c-divider); +} + +/* Match the logo in light mode; keep the upstream dark palette. */ +:root:not(.dark) { + --vp-c-brand: var(--julia-green); + --vp-c-brand-1: #389826; + --vp-c-brand-2: #2f8420; + --vp-c-brand-3: #277018; + --vp-c-brand-light: #389826; +} + +/* Add category-colored titles, icons and left borders to admonitions. */ +.vp-doc .custom-block:not(.jldocstring) { + --mpskit-admonition-accent: var(--vp-c-default-1); + padding: 14px 16px; + border-left-width: 4px; + border-left-color: var(--mpskit-admonition-accent); + color: var(--vp-c-text-1); +} + +.custom-block:not(.jldocstring) .custom-block-title { + display: flex; + align-items: center; + gap: 0.5em; + margin-bottom: 8px; + font-weight: 700; + color: var(--mpskit-admonition-accent); +} + +.custom-block:not(.jldocstring) .custom-block-title::before { + content: ""; + flex: none; + width: 1.15em; + height: 1.15em; + background-color: currentColor; + /* fa-circle-exclamation, the icon Documenter puts on every admonition */ + --mpskit-admonition-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20Zm-1 5h2v7h-2Zm0 9h2v2h-2Z'/%3E%3C/svg%3E"); + -webkit-mask: var(--mpskit-admonition-icon) center / contain no-repeat; + mask: var(--mpskit-admonition-icon) center / contain no-repeat; +} + +/* DVP maps notes to tips and overrides their dark background to gray. */ +.vp-doc .custom-block.tip:not(.jldocstring) { + --mpskit-admonition-accent: var(--vp-c-tip-1); + background-color: var(--vp-c-tip-soft); +} +.vp-doc .custom-block.warning:not(.jldocstring) { + --mpskit-admonition-accent: var(--vp-c-warning-1); +} +.vp-doc .custom-block.danger:not(.jldocstring) { + --mpskit-admonition-accent: var(--vp-c-danger-1); +} +.vp-doc .custom-block.caution:not(.jldocstring) { + --mpskit-admonition-accent: var(--vp-c-caution-1); +} +.vp-doc .custom-block.important:not(.jldocstring) { + --mpskit-admonition-accent: var(--vp-c-important-1); +} + +/* Keep transparent line drawings legible in dark mode. */ +.color-invertible { + transition: filter 0.2s ease; +} +.dark .color-invertible { + filter: invert(1) hue-rotate(180deg); +} diff --git a/docs/src/assets/favicon.ico b/docs/src/public/favicon.ico similarity index 100% rename from docs/src/assets/favicon.ico rename to docs/src/public/favicon.ico diff --git a/docs/src/assets/logo.svg b/docs/src/public/logo.svg similarity index 100% rename from docs/src/assets/logo.svg rename to docs/src/public/logo.svg