diff --git a/.github/workflows/docs-theme.yml b/.github/workflows/docs-theme.yml new file mode 100644 index 0000000..711ae9e --- /dev/null +++ b/.github/workflows/docs-theme.yml @@ -0,0 +1,119 @@ +name: Documentation theme + +on: + push: + branches: [main] + paths: ['index.html', 'docs-theme/**', 'tests/**', '.github/workflows/docs-theme.yml'] + pull_request: + paths: ['index.html', 'docs-theme/**', 'tests/**', '.github/workflows/docs-theme.yml'] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: docs-theme-${{ github.ref }} + cancel-in-progress: true + +jobs: + compatibility: + name: ${{ matrix.project }} (${{ matrix.sha }}) + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + fail-fast: false + max-parallel: 2 + matrix: + include: + - project: pypto + sha: 812628941a4ad118321e097ced984e9ec383203e + python: '3.11' + page: api/tile/index.html + - project: simpler + sha: 405b5bbda30f6d0f6167e4b2f283df2d42ca3443 + python: '3.10' + page: user/reference/api/worker/index.html + - project: pypto-lib + sha: 216b497e4b7d8bb7b47e304e79fd59e01de2a2e9 + python: '3.10' + page: get-started/first-kernel/index.html + - project: pypto-serving + sha: 967c839c22402cb3f23c7430129ce539a0accbdf + python: '3.11' + page: user-guide/online-serving/index.html + - project: pypto-tools + sha: 955cfcfc01c7028f0e9bef01b3fdabc04e90de9e + python: '3.11' + page: runtime/chip-swimlane/index.html + steps: + - uses: actions/checkout@v4 + with: + path: theme + - name: Check out fixed downstream snapshot + uses: actions/checkout@v4 + with: + repository: hw-native-sys/${{ matrix.project }} + ref: ${{ matrix.sha }} + path: downstream + persist-credentials: false + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + - name: Install shared renderer + run: python -m pip install -r theme/docs-theme/constraints.txt + - name: Prepare candidate theme configuration + run: >- + python theme/tests/docs_theme_compat.py prepare + --repo downstream --project '${{ matrix.project }}' + --expected-sha '${{ matrix.sha }}' + - name: Install project documentation plugins + # Toolkit's temporary requirements test the 9.7.7 candidate explicitly; + # no upstream dependency file or source config is changed. + run: >- + python -m pip install -c theme/docs-theme/constraints.txt + -r downstream/requirements-theme-compat.txt + - name: Record tested source and toolchain + run: | + git -C downstream rev-parse HEAD + python --version + python -m pip freeze + python -m pip check + - name: Run existing documentation checks and strict build + working-directory: downstream + env: + DOCS_REF: ${{ matrix.sha }} + run: | + if [ -f .claude/skills/testing/load-env.sh ]; then + source .claude/skills/testing/load-env.sh + fi + for check in check_docs_nav check_public_docs check_docs_en_zh_parity check_docs_symbol_coverage check_op_docstring_parity; do + if [ -f "tests/lint/$check.py" ]; then + python "tests/lint/$check.py" + fi + done + if [ -f tests/docs/test_repo_links.py ]; then + python -m unittest discover -s tests/docs -v + fi + mkdocs build --strict -f mkdocs.theme-compat.yml --site-dir site-theme + - name: Validate rendered branding, links, and assets + run: >- + python theme/tests/docs_theme_compat.py check + --site downstream/site-theme --project '${{ matrix.project }}' + --page '${{ matrix.page }}' + - name: Upload review preview + if: always() + uses: actions/upload-artifact@v4 + with: + name: docs-theme-${{ matrix.project }}-${{ matrix.sha }} + path: downstream/site-theme + retention-days: 7 + - name: Install browser checks + if: matrix.project == 'pypto' + run: | + python -m pip install -r theme/tests/requirements.txt + python -m playwright install --with-deps chromium + - name: Check homepage and bilingual PyPTO in Chromium + if: matrix.project == 'pypto' + env: + PYPTO_DOCS_SITE: ${{ github.workspace }}/downstream/site-theme + run: python -m pytest theme/tests/test_browser_theme.py -q diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6c56ff1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +.pytest_cache/ diff --git a/docs-theme/base.yml b/docs-theme/base.yml new file mode 100644 index 0000000..9283f3a --- /dev/null +++ b/docs-theme/base.yml @@ -0,0 +1,51 @@ +# Paths to the overrides directory belong in the consuming mkdocs.yml. +theme: + name: material + font: false + logo: assets/images/pypto.svg + favicon: assets/images/pypto.svg + features: + - navigation.tabs + - navigation.sections + - navigation.indexes + - navigation.top + - navigation.footer + - content.code.copy + - content.action.edit + - search.suggest + - search.highlight + - toc.follow + # A mapping avoids Material's per-project palette selector. The shared + # selector supports system/light/dark with one preference across projects. + palette: + scheme: default + primary: custom + accent: custom + +extra_css: + - assets/stylesheets/brand.css + - assets/stylesheets/docs.css + +extra: + homepage: https://www.pypto.ai/ + pypto_projects: + - id: pypto + name: PyPTO + url: https://www.pypto.ai/pypto/ + languages: [en, zh] + - id: simpler + name: Simpler + url: https://www.pypto.ai/simpler/ + languages: [en] + - id: pypto-lib + name: PyPTO Lib + url: https://www.pypto.ai/pypto-lib/ + languages: [en] + - id: pypto-serving + name: PyPTO Serving + url: https://www.pypto.ai/pypto-serving/ + languages: [en] + - id: pypto-tools + name: PyPTO Toolkit + url: https://www.pypto.ai/pypto-tools/ + languages: [en, zh] diff --git a/docs-theme/constraints.txt b/docs-theme/constraints.txt new file mode 100644 index 0000000..86bb35b --- /dev/null +++ b/docs-theme/constraints.txt @@ -0,0 +1,3 @@ +# Shared renderer versions. Project-specific plugins remain in each repository. +mkdocs==1.6.1 +mkdocs-material==9.7.7 diff --git a/docs-theme/overrides/assets/images/pypto.svg b/docs-theme/overrides/assets/images/pypto.svg new file mode 100644 index 0000000..fb1d53c --- /dev/null +++ b/docs-theme/overrides/assets/images/pypto.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/docs-theme/overrides/assets/javascripts/preferences.js b/docs-theme/overrides/assets/javascripts/preferences.js new file mode 100644 index 0000000..fee9187 --- /dev/null +++ b/docs-theme/overrides/assets/javascripts/preferences.js @@ -0,0 +1,96 @@ +/* One preference for the homepage, project roots, and translated pages. */ +(() => { + "use strict"; + if (window.PyPTOTheme) return; + + const key = "pypto:color-scheme"; + const modes = new Set(["system", "light", "dark"]); + const system = window.matchMedia("(prefers-color-scheme: dark)"); + function readMode(fallback = "system") { + try { + const saved = window.localStorage.getItem(key); + return modes.has(saved) ? saved : "system"; + } catch { + // Storage may be unavailable in private or embedded browser contexts. + return fallback; + } + } + let mode = readMode(); + + function apply() { + const dark = mode === "dark" || (mode === "system" && system.matches); + document.documentElement.dataset.pyptoTheme = dark ? "dark" : "light"; + document.documentElement.classList.remove("no-js"); + if (document.body) { + document.body.dataset.mdColorScheme = dark ? "slate" : "default"; + } + document.querySelectorAll("[data-pypto-theme-select]").forEach(select => { + select.value = mode; + }); + } + + function closeProjects(restoreFocus = false) { + document.querySelectorAll(".pypto-project-switcher[open]").forEach(menu => { + menu.open = false; + if (restoreFocus) menu.querySelector("summary").focus(); + }); + } + + function updatePage() { + apply(); + closeProjects(); + const search = document.querySelector("[data-md-component='search-query']"); + const project = document.querySelector("[data-pypto-project-name]"); + if (search && project) { + const name = project.dataset.pyptoProjectName; + const label = document.documentElement.lang.startsWith("zh") ? `搜索 ${name}` : `Search ${name}`; + search.placeholder = label; + search.setAttribute("aria-label", label); + } + } + + let connected = false; + window.PyPTOTheme = { + apply, + connectMaterial() { + if (!connected && window.document$) { + connected = true; + window.document$.subscribe(updatePage); + } + updatePage(); + } + }; + + apply(); + document.addEventListener("DOMContentLoaded", updatePage); + document.addEventListener("change", event => { + if (!event.target.matches("[data-pypto-theme-select]")) return; + const selected = event.target.value; + if (!modes.has(selected)) return; + mode = selected; + try { + window.localStorage.setItem(key, mode); + } catch { + // Keep the selection for this page even when it cannot be persisted. + } + apply(); + }); + document.addEventListener("click", event => { + if (!event.target.closest(".pypto-project-switcher")) closeProjects(); + }); + document.addEventListener("keydown", event => { + if (event.key === "Escape") closeProjects(true); + }); + window.addEventListener("storage", event => { + if (event.key !== key && event.key !== null) return; + mode = modes.has(event.newValue) ? event.newValue : "system"; + apply(); + }); + window.addEventListener("pageshow", () => { + mode = readMode(mode); + apply(); + }); + system.addEventListener("change", () => { + if (mode === "system") apply(); + }); +})(); diff --git a/docs-theme/overrides/assets/stylesheets/brand.css b/docs-theme/overrides/assets/stylesheets/brand.css new file mode 100644 index 0000000..9318f0d --- /dev/null +++ b/docs-theme/overrides/assets/stylesheets/brand.css @@ -0,0 +1,78 @@ +/* Shared by the organization homepage and every documentation build. */ +:root { + color-scheme: light; + --pypto-background: #f7f8fc; + --pypto-surface: #ffffff; + --pypto-text: #192039; + --pypto-muted: #59627a; + --pypto-line: #dfe3ee; + --pypto-accent: #4352c7; + --pypto-featured: #f0f2ff; + --pypto-shadow: 0 12px 32px rgb(25 32 57 / 6%); + --pypto-font: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + --pypto-code-font: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace; +} + +:root[data-pypto-theme="dark"] { + color-scheme: dark; + --pypto-background: #10121b; + --pypto-surface: #191d2b; + --pypto-text: #edf0ff; + --pypto-muted: #aab3cc; + --pypto-line: #343b53; + --pypto-accent: #a8b2ff; + --pypto-featured: #20263e; + --pypto-shadow: 0 12px 32px rgb(0 0 0 / 18%); +} + +@media (prefers-color-scheme: dark) { + :root:not([data-pypto-theme]) { + color-scheme: dark; + --pypto-background: #10121b; + --pypto-surface: #191d2b; + --pypto-text: #edf0ff; + --pypto-muted: #aab3cc; + --pypto-line: #343b53; + --pypto-accent: #a8b2ff; + --pypto-featured: #20263e; + --pypto-shadow: 0 12px 32px rgb(0 0 0 / 18%); + } +} + +.pypto-theme-control { + display: inline-flex; + align-items: center; + gap: 6px; + color: var(--pypto-muted); + font: 14px var(--pypto-font); +} + +.pypto-theme-control select { + min-height: 40px; + max-width: 100%; + padding: 6px 8px; + color: var(--pypto-text); + background: var(--pypto-surface); + border: 1px solid var(--pypto-line); + border-radius: 8px; + font: inherit; + cursor: pointer; +} + +.pypto-theme-control select:focus-visible { + outline: 3px solid var(--pypto-accent); + outline-offset: 3px; +} + +.pypto-visually-hidden { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + overflow: hidden; + clip-path: inset(50%); + white-space: nowrap; + border: 0; +} + +.no-js .pypto-theme-control { display: none; } diff --git a/docs-theme/overrides/assets/stylesheets/docs.css b/docs-theme/overrides/assets/stylesheets/docs.css new file mode 100644 index 0000000..6c96bff --- /dev/null +++ b/docs-theme/overrides/assets/stylesheets/docs.css @@ -0,0 +1,114 @@ +/* Material keeps its layout and behavior; these rules supply PyPTO's visuals. */ +:root { + --md-text-font: var(--pypto-font); + --md-code-font: var(--pypto-code-font); +} + +body[data-md-color-scheme] { + --md-default-bg-color: var(--pypto-background); + --md-default-fg-color: var(--pypto-text); + --md-default-fg-color--light: var(--pypto-muted); + --md-default-fg-color--lighter: var(--pypto-line); + --md-primary-fg-color: var(--pypto-accent); + --md-primary-bg-color: var(--pypto-background); + --md-primary-bg-color--light: var(--pypto-muted); + --md-accent-fg-color: var(--pypto-accent); + --md-accent-fg-color--transparent: var(--pypto-featured); + --md-typeset-a-color: var(--pypto-accent); + --md-typeset-color: var(--pypto-text); + --md-admonition-fg-color: var(--pypto-text); + --md-code-bg-color: var(--pypto-surface); + --md-code-fg-color: var(--pypto-text); + --md-footer-bg-color: var(--pypto-surface); + --md-footer-bg-color--dark: var(--pypto-surface); + --md-footer-fg-color: var(--pypto-text); + --md-footer-fg-color--light: var(--pypto-muted); + --md-footer-fg-color--lighter: var(--pypto-muted); + --md-typeset-table-color: var(--pypto-line); + --md-typeset-table-color--light: var(--pypto-featured); + background: var(--pypto-background); +} + +.md-grid { max-width: 72rem; } +.md-header { color: var(--pypto-text); background: var(--pypto-surface); border-bottom: 1px solid var(--pypto-line); box-shadow: none; } +.pypto-header .md-header__inner { min-height: 3.6rem; gap: .6rem; padding: .55rem .8rem; } +.pypto-brand { display: inline-flex; flex-shrink: 0; align-items: center; gap: .5rem; font-size: 1.05rem; font-weight: 750; letter-spacing: -.04em; } +.pypto-brand img { width: 1.8rem; height: 1.8rem; } +.pypto-header-spacer { flex: 1; } +.pypto-header .md-header__button { margin: 0; } +.pypto-header .md-select__inner { inset-inline: auto 0; } +.pypto-source { display: inline-flex; align-items: center; justify-content: center; min-width: 2rem; min-height: 2rem; } +.pypto-source svg { width: 1rem; height: 1rem; } +.pypto-project-switcher { position: relative; min-width: 0; color: var(--pypto-text); } +.pypto-project-switcher summary { display: flex; align-items: center; gap: .5rem; min-height: 2rem; padding: .35rem .6rem; border: 1px solid var(--pypto-line); border-radius: .4rem; cursor: pointer; list-style: none; font-size: .75rem; font-weight: 600; } +.pypto-project-switcher summary::-webkit-details-marker { display: none; } +.pypto-project-switcher summary > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.pypto-project-switcher summary > svg { flex-shrink: 0; } +.pypto-project-menu { position: absolute; top: calc(100% + .55rem); left: 0; z-index: 10; width: 15rem; max-width: calc(100vw - 2rem); padding: .5rem; background: var(--pypto-surface); border: 1px solid var(--pypto-line); border-radius: .6rem; box-shadow: var(--pypto-shadow); } +.pypto-project-menu p { margin: .3rem .5rem .5rem; color: var(--pypto-muted); font-size: .65rem; } +.pypto-project-menu a { display: flex; justify-content: space-between; align-items: center; gap: .6rem; min-height: 2.2rem; padding: .45rem .55rem; border-radius: .3rem; font-size: .75rem; } +.pypto-project-menu a:hover, .pypto-project-menu a[aria-current] { color: var(--pypto-accent); background: var(--pypto-featured); } +.pypto-project-menu small { color: var(--pypto-muted); font-size: .6rem; } +.pypto-project-menu .pypto-project-home { margin-top: .4rem; border-top: 1px solid var(--pypto-line); border-radius: 0; } +.md-tabs { background: var(--pypto-surface); border-bottom: 1px solid var(--pypto-line); } +.md-tabs__link { font-size: .75rem; opacity: 1; color: var(--pypto-muted); } +.md-tabs__link--active, .md-tabs__item--active .md-tabs__link { color: var(--pypto-accent); font-weight: 650; } +.md-tabs__link:hover { color: var(--pypto-accent); } +.md-tabs__item--active { border-bottom: 2px solid var(--pypto-accent); } +.md-main__inner { margin-top: 1.6rem; } +.md-content { min-width: 0; } +.md-content__inner { margin-inline: 1.6rem; padding-bottom: 2rem; } +.md-typeset { font-size: .8rem; line-height: 1.75; } +.md-typeset h1 { color: var(--pypto-text); font-size: 1.9rem; font-weight: 700; letter-spacing: -.045em; line-height: 1.25; } +.md-typeset h2 { font-weight: 650; letter-spacing: -.025em; } +.md-typeset h3 { font-weight: 650; } +.md-typeset a:hover { text-decoration: underline; text-underline-offset: .15em; } +.md-typeset .md-button { border-radius: .4rem; } +.md-typeset .md-button--primary { color: var(--pypto-background); background: var(--pypto-accent); } +.md-typeset code { border-radius: .2rem; } +.md-typeset pre > code { padding: 1rem; border: 1px solid var(--pypto-line); border-radius: .5rem; } +.md-typeset pre { max-width: 100%; } +.md-typeset .highlight { max-width: 100%; } +.md-typeset table:not([class]) { border-radius: .4rem; background: var(--pypto-surface); } +.md-typeset table:not([class]) th { background: var(--pypto-featured); font-weight: 650; } +.md-typeset .admonition, .md-typeset details { border-radius: .4rem; font-size: .75rem; } +.md-nav { font-size: .7rem; line-height: 1.5; } +.md-nav__link { padding-block: .15rem; } +.md-nav__link--active { color: var(--pypto-accent); font-weight: 650; } +.md-nav__title { color: var(--pypto-text); background: transparent; box-shadow: none; } +.md-sidebar--secondary .md-nav { border-left: 1px solid var(--pypto-line); padding-left: .7rem; } +.md-search__form { background: var(--pypto-background); border: 1px solid var(--pypto-line); border-radius: .4rem; } +.md-search__input { color: var(--pypto-text); } +.md-search__input::placeholder { color: var(--pypto-muted); } +.md-search__icon { color: var(--pypto-muted); } +.md-footer { border-top: 1px solid var(--pypto-line); } +html .md-typeset a:focus-visible, .pypto-header a:focus-visible, .pypto-header summary:focus-visible { outline: 3px solid var(--pypto-accent); outline-offset: 3px; } + +@media (max-width: 76.234375em) { + .md-nav--primary .md-nav__title { background: var(--pypto-surface); color: var(--pypto-text); } + .md-content__inner { margin-inline: 1.2rem; } +} + +@media (max-width: 59.984375em) { + .pypto-header .md-header__inner { min-height: 3.2rem; gap: .4rem; } + .pypto-header-spacer { display: none; } + .pypto-project-switcher { flex: 1; } + .pypto-project-switcher summary { justify-content: space-between; } + .pypto-project-menu { left: auto; right: 0; } +} + +@media (max-width: 600px) { + .pypto-brand span, .pypto-source { display: none; } + .pypto-header .md-header__inner { flex-wrap: wrap; gap: .25rem; padding-inline: .4rem; } + .pypto-brand img { width: 1.5rem; height: 1.5rem; } + .pypto-project-switcher { flex: 1 1 3rem; } + .pypto-project-switcher summary { max-width: 11rem; } + .pypto-project-menu { position: fixed; top: auto; left: .5rem; right: .5rem; width: auto; max-width: none; margin-top: .5rem; } + .pypto-theme-control select { padding-inline: 3px; font-size: 14px; } + .md-content__inner { margin-inline: .8rem; } + .md-typeset h1 { font-size: 1.6rem; } +} + +@media (prefers-reduced-motion: reduce) { + .pypto-header * { scroll-behavior: auto; transition: none; } +} diff --git a/docs-theme/overrides/main.html b/docs-theme/overrides/main.html new file mode 100644 index 0000000..8e2347b --- /dev/null +++ b/docs-theme/overrides/main.html @@ -0,0 +1,17 @@ +{% extends "base.html" %} + +{% block extrahead %} + {{ super() }} + + +{% endblock %} + +{% block header %} + + {% include "partials/pypto-header.html" %} +{% endblock %} + +{% block scripts %} + {{ super() }} + +{% endblock %} diff --git a/docs-theme/overrides/partials/project-switcher.html b/docs-theme/overrides/partials/project-switcher.html new file mode 100644 index 0000000..f4a6d63 --- /dev/null +++ b/docs-theme/overrides/partials/project-switcher.html @@ -0,0 +1,20 @@ +{% set chinese = lang.t('language').startswith('zh') %} +
+ + {{ config.site_name }} + {{ '切换项目' if chinese else 'Switch project' }} + + + +
diff --git a/docs-theme/overrides/partials/pypto-header.html b/docs-theme/overrides/partials/pypto-header.html new file mode 100644 index 0000000..9ff8ce3 --- /dev/null +++ b/docs-theme/overrides/partials/pypto-header.html @@ -0,0 +1,40 @@ +{% set chinese = lang.t('language').startswith('zh') %} +
+ + {% if 'navigation.tabs.sticky' in features and 'navigation.tabs' in features %} + {% include "partials/tabs.html" %} + {% endif %} +
diff --git a/docs/theme-maintenance.md b/docs/theme-maintenance.md new file mode 100644 index 0000000..a067850 --- /dev/null +++ b/docs/theme-maintenance.md @@ -0,0 +1,136 @@ +# Shared documentation theme + +The homepage and project documentation share the brand colors, logo, and color +preference in `docs-theme/`. Each project builds its own Material for MkDocs site, +including a local copy of the theme assets. Changes here reach a project only +after that project's pinned theme revision is updated and its site is deployed. + +## Files and responsibilities + +- `docs-theme/base.yml`: common appearance and the five-project directory. +- `docs-theme/constraints.txt`: the supported MkDocs and Material versions. +- `docs-theme/overrides/main.html`: extends Material's supported template blocks. +- `docs-theme/overrides/partials/`: the shared header and project selector. +- `docs-theme/overrides/assets/stylesheets/brand.css`: homepage/documentation + tokens and the theme selector; `docs.css` contains Material-specific styling. +- `docs-theme/overrides/assets/javascripts/preferences.js`: a shared + `pypto:color-scheme` preference with system, light, and dark modes. Unavailable + storage falls back to system colors. Instant navigation reinitializes controls + through Material's `document$` lifecycle. +- `.github/workflows/docs-theme.yml`: builds reference snapshots of all five + projects against a candidate theme. It never deploys their documentation. + +Keep navigation, language plugins, API plugins, link hooks, and content validation +in the consuming repository. Material configuration inheritance replaces lists; +it does not append to them. In particular, do not move a project's `nav` into this +repository: existing navigation checks read its root `mkdocs.yml` directly. + +## Integrating a project + +Record the full shared repository commit in `docs/theme-revision.txt` and ignore +`.site-theme/` in the project's `.gitignore`. Fetch that exact revision locally +and in the project's documentation workflow: + +```bash +set -euo pipefail +git init .site-theme +git -C .site-theme fetch --depth 1 \ + https://github.com/hw-native-sys/hw-native-sys.github.io.git \ + "$(cat docs/theme-revision.txt)" +git -C .site-theme checkout --detach FETCH_HEAD +``` + +Add the common constraint file to the project's documentation requirements. For +`docs/requirements.txt`, the relative include is: + +```text +-c ../.site-theme/docs-theme/constraints.txt +``` + +For Toolkit's root-level `requirements-docs.txt`, use +`-c .site-theme/docs-theme/constraints.txt`. Resolve incompatible requirements +before integrating a project. The Toolkit reference snapshot caps Material below +9.7.2; the compatibility matrix explicitly tests a candidate 9.7.7 requirement +without changing that upstream repository's policy. + +Use this configuration fragment, retaining the project's existing content and +plugins. Paths are resolved relative to the project's main `mkdocs.yml`: + +```yaml +INHERIT: .site-theme/docs-theme/base.yml +site_name: PyPTO +site_url: https://www.pypto.ai/pypto/ +theme: + custom_dir: .site-theme/docs-theme/overrides +extra: + pypto_project: pypto +``` + +The project identifier must match an entry in `extra.pypto_projects`. The shared +header keeps search within the current project. From a Chinese page, the project +selector links to another project's Chinese homepage when available and labels +English-only destinations explicitly. Project switching does not attempt to map +individual articles across repositories. + +Common features are inherited when the local `theme.features` is omitted. A +project retaining optional features such as instant navigation must provide the +complete feature list, including the shared navigation features. Local plugin, +hook, and stylesheet lists likewise need to preserve every required entry. + +Install only the documentation dependencies and build from that checkout: + +```bash +python -m pip install -r docs/requirements.txt +mkdocs build --strict +mkdocs serve +``` + +Keep each project's existing documentation checks and artifact directory. This +workflow does not require compiling PyPTO, installing CANN, or running device +tests; API documentation is extracted from Python source. + +## Validating and releasing + +The compatibility workflow builds fixed snapshots of all five projects and +checks their rendered root and deep pages. Its PyPTO job also runs Chromium +checks against the homepage and the generated English/Chinese documentation. +To run those browser checks locally after building PyPTO: + +```bash +python -m pip install -r tests/requirements.txt +python -m playwright install chromium +PYPTO_DOCS_SITE=/path/to/pypto/site python -m pytest tests/test_browser_theme.py -q +``` + +`tests/docs_theme_compat.py --help` describes the standalone candidate-build +and rendered-output checks used by CI. + +The unchanged PyPTO reference build with Material 9.7.7 has two known i18n +limitations: optional alternate-page sitemap requests return 404, and a search +result in the other language can redirect to that language's homepage. The +browser suite checks navigation to results in the current language and permits +only those known optional sitemap responses. Shared assets, other HTTP errors, +and JavaScript errors remain checked. Revisit these baseline limitations when +upgrading Material or the i18n plugin. + +1. Run the compatibility workflow and record the exact downstream source + revisions. Update its reference snapshots deliberately as project APIs evolve. +2. Inspect generated English and Chinese homepages, deep articles, and API pages. + Check the project selector, search, language switching, code copy, keyboard + focus, and color preference across page and project changes. +3. Check desktop, tablet, and narrow mobile widths, both color schemes, and text + enlargement. Code blocks and tables may scroll; the whole page must not. +4. Merge the shared theme, then update each consumer's revision file to the + tested commit. A commit retained through a normal merge can remain pinned; + after a squash, use and validate the resulting commit instead. +5. Merge each consumer independently, wait for its Pages deployment, and verify + the deployed assets. The `pypto-docs-theme` metadata identifies this theme. + +Roll back a consumer by restoring its previous revision file **and any dependency +changes required by that revision**, then rebuilding and deploying it. Reverting +only this repository does not change already deployed consumer sites. + +Prefer small template extensions over copying Material's templates. When updating +Material, verify the header's drawer, search, language, and `data-md-component` +contracts as well as the appearance; a successful HTML build alone cannot check +the browser behavior. diff --git a/index.html b/index.html index b24bbf8..da565e0 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,5 @@ - + @@ -8,18 +8,20 @@ PyPTO — Projects & Documentation + + +