Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ MPSKit = "bb1c41ca-d63c-52ed-829e-0820dda26502"
MPSKitModels = "ca635005-6f8c-4cd1-b51d-8491250ef2ab"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Polynomials = "f27b6e38-b328-58d1-80ce-0feddd5e7a45"
SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce"
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
TensorKit = "07d1fe3e-3e46-537d-9eac-e9e13d0d4cec"
TensorKitTensors = "41b62e7d-e9d1-4e23-942c-79a97adf954b"
TensorOperations = "6aa20fa7-93e2-5fca-9bc0-fbd0db3c71a2"

[sources]
Expand All @@ -22,3 +24,4 @@ MPSKit = {path = ".."}
Documenter = "1.11"
DocumenterInterLinks = "1"
DocumenterVitepress = "0.3"
TensorKitTensors = "0.2"
19 changes: 19 additions & 0 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,25 @@ using Documenter
using DocumenterVitepress
using DocumenterCitations
using DocumenterInterLinks
using SHA: sha256

# `src/.vitepress/config.mts` is vendored from the DocumenterVitepress template (see the
# header comment there) so that we can hook `themeConfig.search` and `markdown.config`,
# which `MarkdownVitepress` does not expose. Warn when upstream changes the template, so
# that our copy can be re-synced — or dropped, once the fixes land upstream.
let template = joinpath(pkgdir(DocumenterVitepress), "template", "src", ".vitepress", "config.mts")
# Templates the vendored copy is known to be a faithful superset of; v0.3.4 and v0.3.5
# differ only by the (inert for us) NOINDEX marker.
vendored_from = (
"ca5a958eb398b3219557633f017467cfa07f4882dc2dfe55b12d1f6c0e70d729", # v0.3.4
"56289223983a3844417eae597f81da878f395792a529721e7873178c92d60721", # v0.3.5
)
actual = bytes2hex(sha256(read(template)))
actual in vendored_from || @warn """
DocumenterVitepress' `config.mts` template has changed since `docs/src/.vitepress/config.mts` \
was vendored from it. Re-sync the vendored copy (keeping the `MPSKit:` additions), or delete \
it if upstream now ships them.""" template actual
end

# examples
example_dir = joinpath(@__DIR__, "src", "examples")
Expand Down
202 changes: 202 additions & 0 deletions docs/src/.vitepress/config.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
// ============================================================================
// VENDORED FILE — keep in sync with DocumenterVitepress.
//
// This is a verbatim copy of the DocumenterVitepress **v0.3.5** template at
// `template/src/.vitepress/config.mts`, plus the MPSKit additions marked with
// `MPSKit:` below. DocumenterVitepress copies this file into the build as-is
// and then performs its `REPLACE_ME_DOCUMENTER_VITEPRESS*` substitutions on it
// (see `modify_config_file` in DocumenterVitepress/src/vitepress_config.jl), so
// the nav, sidebar, base, outDir, ... all keep working.
//
// We only vendor it because `themeConfig.search.options` and `markdown.config`
// are not reachable from `docs/make.jl`. Both additions below belong upstream
// in DocumenterVitepress — see `docs/UPSTREAM_DVP_NOTES.md`. Once upstream
// ships them, DELETE this file and the drift guard in `docs/make.jl`.
//
// `docs/make.jl` hashes the installed DocumenterVitepress template and warns if
// it has changed, which means this copy needs re-syncing.
// ============================================================================

import { defineConfig } from 'vitepress'
import { tabsMarkdownPlugin } from 'vitepress-plugin-tabs'
import { mathjaxPlugin } from './mathjax-plugin'
import { juliaReplTransformer } from './julia-repl-transformer'
import footnote from "markdown-it-footnote";
import path from 'path'

const mathjax = mathjaxPlugin()

function getBaseRepository(base: string): string {
if (!base || base === '/') return '/';
const parts = base.split('/').filter(Boolean);
return parts.length > 0 ? `/${parts[0]}/` : '/';
}

// MPSKit: ---------------------------------------------------------------
// DocumenterVitepress renders each docstring as a raw `<details>` block whose
// anchor lives in the `<summary>`:
//
// <summary><a id='MPSKit.FiniteMPS-lib-states' href='#MPSKit.FiniteMPS-lib-states'>
// <span class="jlbinding">MPSKit.FiniteMPS</span></a> <Badge ... /></summary>
//
// VitePress' local search only splits a page into indexable sections at
// headings (`<hN>...<a href="#...">...</a></hN>`), so an `@autodocs` page is
// indexed as one single huge document: docstrings rank poorly against short
// manual sections and every hit links to the top of the page. Rewriting each
// summary into a heading *for indexing only* gives one search entry per
// docstring, titled with the binding and deep-linking to its anchor.
// See QuantumKitHub/MPSKit.jl#478.
//
// The replacement has to mirror the shape VitePress' own heading anchors have,
// because the indexer reads the section title from the text *before* the anchor
// (`headingContentRegex = /(.*?)<a.*? href="#(.*?)".*?>.*?<\/a>/i`) and drops any
// section whose title comes out empty.
const DOCSTRING_SUMMARY =
/<summary><a id='([^']+)' href='([^']+)'><span class="jlbinding">(.*?)<\/span><\/a>.*?<\/summary>/g

// MPSKit: ---------------------------------------------------------------
// Documenter rewrites every markdown heading inside a docstring into a
// bold-only paragraph before any writer sees it (`recursive_heading_to_bold!`
// in Documenter/src/expander_pipeline.jl), so `# Constructors` reaches
// VitePress as `<p><strong>Constructors</strong></p>` with nothing to style.
// Tag exactly those paragraphs so `theme/custom.css` can render them as
// section headings again. See QuantumKitHub/MPSKit.jl#477.
//
// Scoped to docstring `<details>` blocks, and requires the paragraph to consist
// of nothing but one `<strong>`, so that a paragraph merely *starting* with
// bold text (e.g. "**Not every algorithm ...** — see the table below") is left
// alone.
function docstringHeadings(md) {
md.core.ruler.push('mpskit_docstring_headings', (state) => {
const tokens = state.tokens
let depth = 0
for (let i = 0; i < tokens.length; i++) {
const token = tokens[i]

if (token.type === 'html_block') {
if (/<details[^>]*\bclass=['"][^'"]*\bjldocstring\b/.test(token.content)) depth++
else if (depth > 0 && /<\/details>/.test(token.content)) depth--
continue
}

if (depth === 0 || token.type !== 'paragraph_open') continue
const inline = tokens[i + 1]
if (!inline || inline.type !== 'inline' || !inline.children) continue

// markdown-it emits empty `text` tokens around inline markup.
const children = inline.children.filter(
(c) => !(c.type === 'text' && c.content === '')
)
if (children.length < 2) continue
if (children[0].type !== 'strong_open') continue
if (children[children.length - 1].type !== 'strong_close') continue

token.attrJoin('class', 'jldocstring-heading')
}
})
}
// -----------------------------------------------------------------------

const baseTemp = {
base: 'REPLACE_ME_DOCUMENTER_VITEPRESS',// TODO: replace this in makedocs!
}

const navTemp = {
nav: 'REPLACE_ME_DOCUMENTER_VITEPRESS',
}

const nav = [
...navTemp.nav,
{
component: 'VersionPicker'
}
]

// https://vitepress.dev/reference/site-config
export default defineConfig({
base: 'REPLACE_ME_DOCUMENTER_VITEPRESS',// TODO: replace this in makedocs!
title: 'REPLACE_ME_DOCUMENTER_VITEPRESS',
description: 'REPLACE_ME_DOCUMENTER_VITEPRESS',
lastUpdated: true,
cleanUrls: true,
outDir: 'REPLACE_ME_DOCUMENTER_VITEPRESS', // This is required for MarkdownVitepress to work correctly...
head: [
['link', { rel: 'icon', href: 'REPLACE_ME_DOCUMENTER_VITEPRESS_FAVICON' }],
['script', {src: `${getBaseRepository(baseTemp.base)}versions.js`}],
// ['script', {src: '/versions.js'], for custom domains, I guess if deploy_url is available.
['script', {src: `${baseTemp.base}siteinfo.js`}],
// REPLACE_ME_DOCUMENTER_VITEPRESS_NOINDEX
],

markdown: {
codeTransformers: [juliaReplTransformer()],
config(md) {
md.use(tabsMarkdownPlugin);
md.use(footnote);
mathjax.markdownConfig(md);
md.use(docstringHeadings); // MPSKit: see above
},
theme: {
light: "github-light",
dark: "github-dark"
},
},
vite: {
plugins: [
mathjax.vitePlugin,
],
define: {
__DEPLOY_ABSPATH__: JSON.stringify('REPLACE_ME_DOCUMENTER_VITEPRESS_DEPLOY_ABSPATH'),
},
resolve: {
alias: {
'@': path.resolve(__dirname, '../components')
}
},
optimizeDeps: {
exclude: [
'@nolebase/vitepress-plugin-enhanced-readabilities/client',
'vitepress',
'@nolebase/ui',
],
},
ssr: {
noExternal: [
// If there are other packages that need to be processed by Vite, you can add them here.
'@nolebase/vitepress-plugin-enhanced-readabilities',
'@nolebase/ui',
],
},
},
themeConfig: {
outline: 'deep',
logo: 'REPLACE_ME_DOCUMENTER_VITEPRESS',
search: {
provider: 'local',
options: {
detailedView: true,
// MPSKit: index every docstring as its own section — see above.
_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) =>
`<h3 id="${id}">${name} <a class="header-anchor" href="${href}">&#8203;</a></h3>`
)
}
}
},
nav,
sidebar: 'REPLACE_ME_DOCUMENTER_VITEPRESS',
sidebarDrawer: 'REPLACE_ME_DOCUMENTER_VITEPRESS_SIDEBAR_DRAWER',
editLink: 'REPLACE_ME_DOCUMENTER_VITEPRESS',
socialLinks: [
{ icon: 'github', link: 'REPLACE_ME_DOCUMENTER_VITEPRESS' }
],
footer: {
message: 'Made with <a href="https://luxdl.github.io/DocumenterVitepress.jl/dev/" target="_blank"><strong>DocumenterVitepress.jl</strong></a><br>',
copyright: `© Copyright ${new Date().getUTCFullYear()}.`
}
}
})
103 changes: 103 additions & 0 deletions docs/src/.vitepress/theme/custom.css
Original file line number Diff line number Diff line change
@@ -1,3 +1,45 @@
/* ===== Bold text on Linux =====
VitePress' reset sets `font-synthesis: style` on <body>, permitting a synthesised
oblique but not a synthesised weight. That is fine wherever the resolved font has
a real bold face — as `-apple-system` and `Segoe UI` do — but the
DocumenterVitepress font stack names three families it never loads ("Barlow",
"Inter var experimental", "Inter var"), and on Linux fontconfig satisfies those
*and* the platform names after them with a single regular face. `font-weight: 700`
then computes correctly and still renders at regular weight, which is why bold
docstring headings, admonition titles and inline <strong> read as plain text on
Linux while being fine on macOS and Windows (QuantumKitHub/MPSKit.jl#477).

Allowing weight synthesis fixes that without touching the font stack, so nothing
changes on platforms that already resolve a real bold face. Proposed upstream; drop
this once DocumenterVitepress ships it. Note that the deeper problem is separate:
VitePress self-hosts Inter with a real 100–900 weight axis, but the stack above
names the family names it used before v4 renamed the face to plain "Inter"
(vuejs/vitepress#3694), so that font is downloaded and never used. Naming it would
give real bold faces instead of synthesised ones, but would also change body text
on every platform — a look-and-feel decision for upstream, not a bug fix. */
body {
font-synthesis: weight style;
}

/* ===== Docstring section headings =====
Documenter rewrites markdown headings inside docstrings into bold-only
paragraphs (`recursive_heading_to_bold!`), so there is no <h*> to style. The
markdown-it rule in `.vitepress/config.mts` tags exactly those paragraphs;
give them a heading-like treatment — bold, plus a light rule beneath. */
.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);
}

.jldocstring.custom-block p.jldocstring-heading strong {
font-weight: inherit;
}

/* ===== MPSKit branding ===== */
/* Accent color matching the (green) MPSKit logo in light mode.
Dark mode already uses the green palette defined by the DVP template. */
Expand All @@ -9,6 +51,67 @@
--vp-c-brand-light: #389826;
}

/* ===== Admonitions =====
Documenter draws `!!! note` / `!!! warning` as a bordered box with a bold,
category-coloured, icon-marked header. DocumenterVitepress maps them onto
VitePress custom blocks, whose title is neither coloured nor icon-marked, and
whose border is transparent — so the blocks stop reading as callouts. Worse,
DVP maps every `note` onto the `tip` container and then restyles `tip` to a
flat grey box in dark mode, so the docs' notes lose their colour entirely.

Restore a Documenter-like treatment: accent-coloured bold title with an icon,
a coloured left rule, and body text at full contrast. `:not(.jldocstring)`
keeps this off docstrings, which also carry the `custom-block` class. */
.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;
}

/* `note` and `tip` are indistinguishable by the time the markdown reaches us —
DVP maps both onto the `tip` container — so they share the tip palette. The
background is restated because the DVP template overrides it to a flat grey in
dark mode, which is what drained the colour out of every `!!! note`. */
.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);
}

/* ===== Color-invertible diagrams =====
The manual embeds line-drawing diagrams (black on transparent) via
`@raw html` <img class="color-invertible">. Legible on a light background
Expand Down
File renamed without changes.
File renamed without changes
Loading