mirror of
https://github.com/orbbec/OrbbecSDK_ROS2.git
synced 2026-10-04 12:07:46 +08:00
docs: implement versioning system and enhance documentation structure
This commit is contained in:
@@ -4,6 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- docs/format_optimization
|
- docs/format_optimization
|
||||||
|
- docs/version_optimization
|
||||||
- Document
|
- Document
|
||||||
- main
|
- main
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -22,6 +23,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v5
|
uses: actions/setup-python@v5
|
||||||
@@ -35,17 +38,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build multilingual HTML
|
- name: Build multilingual HTML
|
||||||
run: |
|
run: |
|
||||||
# Build English
|
python docs/build_versions.py
|
||||||
sphinx-build -b html docs/en docs/en/_build/html
|
|
||||||
# Build Chinese
|
|
||||||
sphinx-build -b html docs/zh docs/zh/_build/html
|
|
||||||
# Merge outputs to unified publish directory
|
|
||||||
mkdir -p site/en site/zh
|
|
||||||
cp -r docs/en/_build/html/. site/en/
|
|
||||||
cp -r docs/zh/_build/html/. site/zh/
|
|
||||||
# Root redirect to English (可根据需要改成 zh)
|
|
||||||
echo '<!DOCTYPE html><html><head><meta http-equiv="refresh" content="0; url=en/index.html"></head><body>Redirecting...</body></html>' > site/index.html
|
|
||||||
touch site/.nojekyll
|
|
||||||
|
|
||||||
- name: Deploy to gh-pages branch
|
- name: Deploy to gh-pages branch
|
||||||
uses: peaceiris/actions-gh-pages@v4
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
|
|||||||
@@ -1,2 +1,4 @@
|
|||||||
_build
|
_build
|
||||||
.vscode
|
.vscode
|
||||||
|
site/
|
||||||
|
.docs-worktrees/
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
# OrbbecSDK V2 ROS2 Documentation
|
||||||
|
|
||||||
|
This repository contains the documentation for OrbbecSDK V2 ROS2 Wrapper in both English and Chinese.
|
||||||
|
|
||||||
|
## Documentation Structure
|
||||||
|
|
||||||
|
- **English Version**: `docs/en/` - Full English documentation
|
||||||
|
- **中文版本**: `docs/zh/` - 完整中文文档
|
||||||
|
- **Published Versions**: `docs/version-manifest.json` - Version list for GitHub Pages
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Build English Documentation:
|
||||||
|
```bash
|
||||||
|
cd docs/en
|
||||||
|
pip install -r requirements.txt
|
||||||
|
make clean && make html
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Chinese Documentation:
|
||||||
|
```bash
|
||||||
|
cd docs/zh
|
||||||
|
pip install -r requirements.txt
|
||||||
|
make clean && make html
|
||||||
|
```
|
||||||
|
|
||||||
|
### For Full Versioned Site Build:
|
||||||
|
```bash
|
||||||
|
pip install -r docs/en/requirements.txt -r docs/zh/requirements.txt jieba
|
||||||
|
python docs/build_versions.py
|
||||||
|
```
|
||||||
|
|
||||||
|
## View Documentation
|
||||||
|
|
||||||
|
After building, open the following files in your browser:
|
||||||
|
- English: `docs/en/_build/html/index.html`
|
||||||
|
- Chinese: `docs/zh/_build/html/index.html`
|
||||||
|
- Full GitHub Pages site: `site/index.html`
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
docs/
|
||||||
|
├── en/ # English documentation
|
||||||
|
│ ├── source/ # Source files
|
||||||
|
│ ├── conf.py # Sphinx config
|
||||||
|
│ └── requirements.txt # Python dependencies
|
||||||
|
├── zh/ # Chinese documentation
|
||||||
|
│ ├── source/ # Source files
|
||||||
|
│ ├── conf.py # Sphinx config
|
||||||
|
│ └── requirements.txt # Python dependencies
|
||||||
|
├── _ext/ # Shared Sphinx extensions
|
||||||
|
├── build_versions.py # Versioned site builder
|
||||||
|
└── version-manifest.json # Published version definitions
|
||||||
|
```
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
1. Make changes to the English version first (`docs/en/`)
|
||||||
|
2. Translate and adapt for the Chinese version (`docs/zh/`)
|
||||||
|
3. Ensure both versions build successfully
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
- Sphinx
|
||||||
|
- recommonmark
|
||||||
|
- sphinx-rtd-theme
|
||||||
|
- jieba (for Chinese search)
|
||||||
|
- Required extensions (see requirements.txt in each language folder)
|
||||||
@@ -15,22 +15,35 @@ def inject_language_switch(app, doctree, docname):
|
|||||||
other_label = '中文' if is_en else 'English'
|
other_label = '中文' if is_en else 'English'
|
||||||
current_label = 'English' if is_en else '中文'
|
current_label = 'English' if is_en else '中文'
|
||||||
|
|
||||||
# JS now detects /en/ or /zh/ segment and swaps only the first occurrence
|
|
||||||
html = (
|
html = (
|
||||||
'<div class="lang-switch" style="margin:0 0 1em 0; font-size:0.9em;">'
|
'<div class="lang-switch docs-floating-switches__item">'
|
||||||
f'<a class="lang-other" href="#">{other_label}</a> | '
|
f'<a class="lang-other" href="#">{other_label}</a> | '
|
||||||
f'<a class="lang-current" href="#">{current_label}</a>'
|
f'<a class="lang-current" href="#">{current_label}</a>'
|
||||||
'</div>'
|
'</div>'
|
||||||
'<script>(function(){'
|
'<script>(function(){'
|
||||||
|
"function ensureDock(){"
|
||||||
|
" var dock=document.querySelector('[data-docs-switch-dock]');"
|
||||||
|
" if(dock) return dock;"
|
||||||
|
" dock=document.createElement('div');"
|
||||||
|
" dock.className='docs-floating-switches';"
|
||||||
|
" dock.setAttribute('data-docs-switch-dock','');"
|
||||||
|
" document.body.appendChild(dock);"
|
||||||
|
" return dock;"
|
||||||
|
"}"
|
||||||
|
"function mountSwitch(){"
|
||||||
"var p=window.location.pathname;"
|
"var p=window.location.pathname;"
|
||||||
"var isEn=p.indexOf('/en/')!==-1 && p.indexOf('/zh/')===-1;"
|
"var isEn=p.indexOf('/en/')!==-1 && p.indexOf('/zh/')===-1;"
|
||||||
"if(p.indexOf('/zh/')!==-1) isEn=false;"
|
"if(p.indexOf('/zh/')!==-1) isEn=false;"
|
||||||
"var other = isEn ? p.replace(/\\/en\\//, '/zh/') : p.replace(/\\/zh\\//, '/en/');"
|
"var other = isEn ? p.replace(/\\/en\\//, '/zh/') : p.replace(/\\/zh\\//, '/en/');"
|
||||||
"var current=p;"
|
"var current=p;"
|
||||||
"var sw=document.querySelector('.lang-switch'); if(!sw) return;"
|
"var sw=document.querySelector('.lang-switch'); if(!sw) return;"
|
||||||
|
"ensureDock().appendChild(sw);"
|
||||||
"var o=sw.querySelector('.lang-other'); var c=sw.querySelector('.lang-current');"
|
"var o=sw.querySelector('.lang-other'); var c=sw.querySelector('.lang-current');"
|
||||||
"if(isEn){ o.textContent='中文'; c.textContent='English'; } else { o.textContent='English'; c.textContent='中文'; }"
|
"if(isEn){ o.textContent='中文'; c.textContent='English'; } else { o.textContent='English'; c.textContent='中文'; }"
|
||||||
"o.setAttribute('href', other); c.setAttribute('href', current);"
|
"o.setAttribute('href', other); c.setAttribute('href', current);"
|
||||||
|
"}"
|
||||||
|
"if(document.body){ mountSwitch(); }"
|
||||||
|
"else { document.addEventListener('DOMContentLoaded', mountSwitch, {once:true}); }"
|
||||||
'})();</script>'
|
'})();</script>'
|
||||||
)
|
)
|
||||||
doctree.insert(0, nodes.raw('', html, format='html'))
|
doctree.insert(0, nodes.raw('', html, format='html'))
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
.docs-floating-switches {
|
||||||
|
position: fixed;
|
||||||
|
left: 24px;
|
||||||
|
bottom: 24px;
|
||||||
|
z-index: 1100;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.75rem;
|
||||||
|
max-width: calc(100vw - 48px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-floating-switches__item {
|
||||||
|
max-width: min(220px, calc(100vw - 48px));
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-version-switch {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.45rem;
|
||||||
|
min-width: 192px;
|
||||||
|
padding: 0.9rem 1rem;
|
||||||
|
border: 1px solid rgba(15, 23, 42, 0.14);
|
||||||
|
border-radius: 14px;
|
||||||
|
background: rgba(255, 255, 255, 0.96);
|
||||||
|
box-shadow: 0 18px 38px rgba(15, 23, 42, 0.16);
|
||||||
|
backdrop-filter: blur(14px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-version-switch__header {
|
||||||
|
font-size: 0.74rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.08em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #5b6472;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-version-switch__control {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-version-switch__label {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-version-switch__select {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 2.4rem;
|
||||||
|
padding: 0 0.85rem;
|
||||||
|
border: 1px solid #c7d2e0;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: #f8fbff;
|
||||||
|
color: #0f172a;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-version-switch__select:focus {
|
||||||
|
outline: 2px solid rgba(22, 120, 184, 0.22);
|
||||||
|
border-color: #1678b8;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.docs-floating-switches {
|
||||||
|
left: 12px;
|
||||||
|
bottom: 12px;
|
||||||
|
gap: 0.55rem;
|
||||||
|
max-width: calc(100vw - 24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-floating-switches__item {
|
||||||
|
max-width: calc(100vw - 24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-version-switch {
|
||||||
|
min-width: 156px;
|
||||||
|
padding: 0.75rem 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-version-switch__header {
|
||||||
|
font-size: 0.68rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.docs-version-switch__select {
|
||||||
|
min-height: 2.2rem;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
(function () {
|
||||||
|
function ensureDock() {
|
||||||
|
var dock = document.querySelector("[data-docs-switch-dock]");
|
||||||
|
if (dock) {
|
||||||
|
return dock;
|
||||||
|
}
|
||||||
|
|
||||||
|
dock = document.createElement("div");
|
||||||
|
dock.className = "docs-floating-switches";
|
||||||
|
dock.setAttribute("data-docs-switch-dock", "");
|
||||||
|
document.body.appendChild(dock);
|
||||||
|
return dock;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseConfig() {
|
||||||
|
var element = document.getElementById("docs-version-data");
|
||||||
|
if (!element) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(element.textContent);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to parse docs version configuration.", error);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseLocation(config) {
|
||||||
|
var segments = window.location.pathname.split("/").filter(Boolean);
|
||||||
|
var langIndex = segments.findIndex(function (segment) {
|
||||||
|
return segment === "en" || segment === "zh";
|
||||||
|
});
|
||||||
|
|
||||||
|
if (langIndex === -1) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var language = segments[langIndex];
|
||||||
|
var isVersioned = langIndex >= 2 && segments[langIndex - 2] === "versions";
|
||||||
|
var versionSlug = isVersioned ? segments[langIndex - 1] : config.latest_slug;
|
||||||
|
// Keep the project-pages prefix (for example /<repo>/...) when the
|
||||||
|
// current page is served from the latest alias path /<repo>/<lang>/...
|
||||||
|
var baseSegments = isVersioned ? segments.slice(0, langIndex - 2) : segments.slice(0, langIndex);
|
||||||
|
var relativeSegments = segments.slice(langIndex + 1);
|
||||||
|
|
||||||
|
if (!relativeSegments.length) {
|
||||||
|
relativeSegments = ["index.html"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
baseSegments: baseSegments,
|
||||||
|
language: language,
|
||||||
|
relativeSegments: relativeSegments,
|
||||||
|
versionSlug: versionSlug,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPath(baseSegments, versionSlug, latestSlug, language, relativeSegments) {
|
||||||
|
var segments = baseSegments.slice();
|
||||||
|
if (versionSlug !== latestSlug) {
|
||||||
|
segments.push("versions", versionSlug);
|
||||||
|
}
|
||||||
|
segments.push(language);
|
||||||
|
segments = segments.concat(relativeSegments);
|
||||||
|
return "/" + segments.join("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRootPath(baseSegments, versionSlug, latestSlug, language) {
|
||||||
|
var segments = baseSegments.slice();
|
||||||
|
if (versionSlug !== latestSlug) {
|
||||||
|
segments.push("versions", versionSlug);
|
||||||
|
}
|
||||||
|
segments.push(language, "index.html");
|
||||||
|
return "/" + segments.join("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderOptions(select, config, currentSlug) {
|
||||||
|
select.innerHTML = "";
|
||||||
|
|
||||||
|
config.versions.forEach(function (version) {
|
||||||
|
var option = document.createElement("option");
|
||||||
|
option.value = version.slug;
|
||||||
|
option.textContent = version.label;
|
||||||
|
option.selected = version.slug === currentSlug;
|
||||||
|
select.appendChild(option);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function canUseHeadRequest() {
|
||||||
|
return window.location.protocol === "http:" || window.location.protocol === "https:";
|
||||||
|
}
|
||||||
|
|
||||||
|
function pageExists(url) {
|
||||||
|
if (!canUseHeadRequest()) {
|
||||||
|
return Promise.resolve(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(url, {
|
||||||
|
method: "HEAD",
|
||||||
|
cache: "no-store",
|
||||||
|
})
|
||||||
|
.then(function (response) {
|
||||||
|
return response.ok;
|
||||||
|
})
|
||||||
|
.catch(function () {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
|
var config = parseConfig();
|
||||||
|
var widget = document.querySelector("[data-docs-version-switch]");
|
||||||
|
var select = document.getElementById("docs-version-select");
|
||||||
|
|
||||||
|
if (!config || !widget || !select || !config.versions || !config.versions.length) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureDock().appendChild(widget);
|
||||||
|
|
||||||
|
var locationInfo = parseLocation(config);
|
||||||
|
if (!locationInfo) {
|
||||||
|
widget.hidden = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderOptions(select, config, locationInfo.versionSlug);
|
||||||
|
|
||||||
|
select.addEventListener("change", function (event) {
|
||||||
|
var nextSlug = event.target.value;
|
||||||
|
if (!nextSlug || nextSlug === locationInfo.versionSlug) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetUrl = buildPath(
|
||||||
|
locationInfo.baseSegments,
|
||||||
|
nextSlug,
|
||||||
|
config.latest_slug,
|
||||||
|
locationInfo.language,
|
||||||
|
locationInfo.relativeSegments,
|
||||||
|
);
|
||||||
|
var fallbackUrl = buildRootPath(
|
||||||
|
locationInfo.baseSegments,
|
||||||
|
nextSlug,
|
||||||
|
config.latest_slug,
|
||||||
|
locationInfo.language,
|
||||||
|
);
|
||||||
|
|
||||||
|
pageExists(targetUrl).then(function (exists) {
|
||||||
|
window.location.href = exists ? targetUrl : fallbackUrl;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from docutils import nodes
|
||||||
|
|
||||||
|
|
||||||
|
STATIC_DIR = Path(__file__).resolve().parent / "static"
|
||||||
|
|
||||||
|
|
||||||
|
def setup(app):
|
||||||
|
app.connect("config-inited", configure_assets)
|
||||||
|
app.connect("doctree-resolved", inject_version_switch)
|
||||||
|
app.add_css_file("version_switch.css")
|
||||||
|
app.add_js_file("version_switch.js")
|
||||||
|
return {"version": "0.1", "parallel_read_safe": True}
|
||||||
|
|
||||||
|
|
||||||
|
def configure_assets(app, config):
|
||||||
|
static_dir = str(STATIC_DIR)
|
||||||
|
if static_dir not in config.html_static_path:
|
||||||
|
config.html_static_path.append(static_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def inject_version_switch(app, doctree, docname):
|
||||||
|
if app.builder.format != "html":
|
||||||
|
return
|
||||||
|
|
||||||
|
versioning = getattr(app.config, "html_context", {}).get("docs_versioning")
|
||||||
|
if not versioning or not versioning.get("versions"):
|
||||||
|
return
|
||||||
|
|
||||||
|
payload = json.dumps(versioning, ensure_ascii=False).replace("</", "<\\/")
|
||||||
|
current_label = versioning.get("current", {}).get("label", "")
|
||||||
|
|
||||||
|
html = f"""
|
||||||
|
<div class="docs-version-switch" data-docs-version-switch>
|
||||||
|
<div class="docs-version-switch__header">Version</div>
|
||||||
|
<div class="docs-version-switch__control">
|
||||||
|
<label class="docs-version-switch__label" for="docs-version-select">Version</label>
|
||||||
|
<select id="docs-version-select" class="docs-version-switch__select" aria-label="Select documentation version">
|
||||||
|
<option value="">{current_label}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script id="docs-version-data" type="application/json">{payload}</script>
|
||||||
|
"""
|
||||||
|
doctree.insert(0, nodes.raw("", html, format="html"))
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
EXTENSION_DIR = Path(__file__).resolve().parent
|
||||||
|
DOCS_DIR = EXTENSION_DIR.parent
|
||||||
|
DEFAULT_MANIFEST_PATH = DOCS_DIR / "version-manifest.json"
|
||||||
|
|
||||||
|
|
||||||
|
def load_manifest() -> dict:
|
||||||
|
manifest_path = Path(os.getenv("DOCS_VERSION_MANIFEST_JSON", DEFAULT_MANIFEST_PATH)).resolve()
|
||||||
|
return json.loads(manifest_path.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def get_content_root(conf_file: str) -> Path:
|
||||||
|
value = os.getenv("DOCS_CONTENT_ROOT")
|
||||||
|
if value:
|
||||||
|
return Path(value).resolve()
|
||||||
|
return Path(conf_file).resolve().parent
|
||||||
|
|
||||||
|
|
||||||
|
def get_conf_root(conf_file: str) -> Path:
|
||||||
|
return Path(conf_file).resolve().parent
|
||||||
|
|
||||||
|
|
||||||
|
def _unique_paths(paths: list[Path]) -> list[str]:
|
||||||
|
unique: list[str] = []
|
||||||
|
seen: set[str] = set()
|
||||||
|
for path in paths:
|
||||||
|
resolved = str(path.resolve())
|
||||||
|
if resolved in seen or not path.exists():
|
||||||
|
continue
|
||||||
|
seen.add(resolved)
|
||||||
|
unique.append(str(path))
|
||||||
|
return unique
|
||||||
|
|
||||||
|
|
||||||
|
def get_templates_paths(conf_root: Path) -> list[str]:
|
||||||
|
candidates = [
|
||||||
|
conf_root / "_templates",
|
||||||
|
conf_root / "source" / "_templates",
|
||||||
|
]
|
||||||
|
return _unique_paths(candidates)
|
||||||
|
|
||||||
|
|
||||||
|
def get_static_paths(conf_root: Path, content_root: Path) -> list[str]:
|
||||||
|
candidates = [
|
||||||
|
content_root / "source" / "_static",
|
||||||
|
conf_root / "source" / "_static",
|
||||||
|
content_root / "source" / "image",
|
||||||
|
]
|
||||||
|
return _unique_paths(candidates)
|
||||||
|
|
||||||
|
|
||||||
|
def get_extra_paths(content_root: Path) -> list[str]:
|
||||||
|
candidate = content_root / "source" / "image"
|
||||||
|
return _unique_paths([candidate])
|
||||||
|
|
||||||
|
|
||||||
|
def get_asset_path(content_root: Path, relative_path: str, fallback: str) -> str:
|
||||||
|
candidate = content_root / relative_path
|
||||||
|
if candidate.exists():
|
||||||
|
return str(candidate)
|
||||||
|
return fallback
|
||||||
|
|
||||||
|
|
||||||
|
def build_version_context(language: str) -> dict:
|
||||||
|
manifest = load_manifest()
|
||||||
|
published_versions = [
|
||||||
|
{
|
||||||
|
"slug": version["slug"],
|
||||||
|
"label": version.get("label", version["slug"]),
|
||||||
|
"is_latest": bool(version.get("is_latest", False)),
|
||||||
|
}
|
||||||
|
for version in manifest.get("versions", [])
|
||||||
|
if version.get("published", False)
|
||||||
|
]
|
||||||
|
|
||||||
|
current_slug = os.getenv("DOCS_VERSION_SLUG", manifest.get("latest_slug", "latest"))
|
||||||
|
matched = next((version for version in published_versions if version["slug"] == current_slug), None)
|
||||||
|
current_label = os.getenv("DOCS_VERSION_LABEL") or (matched["label"] if matched else current_slug)
|
||||||
|
|
||||||
|
current_is_latest_env = os.getenv("DOCS_VERSION_IS_LATEST")
|
||||||
|
if current_is_latest_env is None:
|
||||||
|
current_is_latest = bool(matched["is_latest"]) if matched else current_slug == manifest.get("latest_slug")
|
||||||
|
else:
|
||||||
|
current_is_latest = current_is_latest_env.lower() in {"1", "true", "yes", "on"}
|
||||||
|
|
||||||
|
return {
|
||||||
|
"default_language": manifest.get("default_language", "en"),
|
||||||
|
"latest_slug": manifest.get("latest_slug", current_slug),
|
||||||
|
"current": {
|
||||||
|
"slug": current_slug,
|
||||||
|
"label": current_label,
|
||||||
|
"is_latest": current_is_latest,
|
||||||
|
"language": language,
|
||||||
|
},
|
||||||
|
"versions": published_versions,
|
||||||
|
}
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
DOCS_DIR = REPO_ROOT / "docs"
|
||||||
|
MANIFEST_PATH = DOCS_DIR / "version-manifest.json"
|
||||||
|
SITE_DIR = REPO_ROOT / "site"
|
||||||
|
WORKTREE_ROOT = REPO_ROOT / ".docs-worktrees"
|
||||||
|
LANGUAGES = ("en", "zh")
|
||||||
|
|
||||||
|
|
||||||
|
def run(command: list[str], *, cwd: Path | None = None, env: dict[str, str] | None = None) -> subprocess.CompletedProcess[str]:
|
||||||
|
print("+", " ".join(command))
|
||||||
|
return subprocess.run(
|
||||||
|
command,
|
||||||
|
cwd=str(cwd or REPO_ROOT),
|
||||||
|
env=env,
|
||||||
|
check=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def capture(command: list[str], *, cwd: Path | None = None) -> str:
|
||||||
|
result = subprocess.run(
|
||||||
|
command,
|
||||||
|
cwd=str(cwd or REPO_ROOT),
|
||||||
|
check=True,
|
||||||
|
text=True,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
return result.stdout.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def load_manifest() -> dict:
|
||||||
|
return json.loads(MANIFEST_PATH.read_text(encoding="utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_ref_available(ref: str) -> None:
|
||||||
|
if ref == "HEAD":
|
||||||
|
return
|
||||||
|
|
||||||
|
if ref_resolves(ref) or resolve_remote_ref(ref) is not None:
|
||||||
|
return
|
||||||
|
|
||||||
|
fetch_targets = [ref]
|
||||||
|
if "/" not in ref and not ref.startswith("refs/"):
|
||||||
|
fetch_targets.append(f"refs/heads/{ref}")
|
||||||
|
|
||||||
|
for remote in list_remotes():
|
||||||
|
for target in fetch_targets:
|
||||||
|
try:
|
||||||
|
run(["git", "fetch", "--no-tags", remote, target])
|
||||||
|
if ref_resolves(ref) or resolve_remote_ref(ref) is not None:
|
||||||
|
return
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
continue
|
||||||
|
|
||||||
|
raise RuntimeError(f"Unable to resolve git ref '{ref}'.")
|
||||||
|
|
||||||
|
|
||||||
|
def list_remotes() -> list[str]:
|
||||||
|
remotes = capture(["git", "remote"]).splitlines()
|
||||||
|
ordered: list[str] = []
|
||||||
|
for candidate in ("origin", "github"):
|
||||||
|
if candidate in remotes:
|
||||||
|
ordered.append(candidate)
|
||||||
|
for remote in remotes:
|
||||||
|
if remote not in ordered:
|
||||||
|
ordered.append(remote)
|
||||||
|
return ordered
|
||||||
|
|
||||||
|
|
||||||
|
def ref_resolves(ref: str) -> bool:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "rev-parse", "--verify", f"{ref}^{{commit}}"],
|
||||||
|
cwd=str(REPO_ROOT),
|
||||||
|
text=True,
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
)
|
||||||
|
return result.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_ref(ref: str) -> str:
|
||||||
|
if ref_resolves(ref):
|
||||||
|
return ref
|
||||||
|
|
||||||
|
remote_ref = resolve_remote_ref(ref)
|
||||||
|
if remote_ref is not None:
|
||||||
|
return remote_ref
|
||||||
|
|
||||||
|
raise RuntimeError(f"Unable to resolve git ref '{ref}'.")
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_remote_ref(ref: str) -> str | None:
|
||||||
|
for remote in list_remotes():
|
||||||
|
remote_ref = f"refs/remotes/{remote}/{ref}"
|
||||||
|
if ref_resolves(remote_ref):
|
||||||
|
return remote_ref
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def should_use_workspace(version: dict) -> bool:
|
||||||
|
flag = os.getenv("DOCS_BUILD_LATEST_FROM_WORKSPACE", "0").lower()
|
||||||
|
return version.get("is_latest", False) and flag in {"1", "true", "yes", "on"}
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_site_dir() -> None:
|
||||||
|
if SITE_DIR.exists():
|
||||||
|
shutil.rmtree(SITE_DIR)
|
||||||
|
SITE_DIR.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def create_root_redirect(default_language: str) -> None:
|
||||||
|
target = f"{default_language}/index.html"
|
||||||
|
(SITE_DIR / "index.html").write_text(
|
||||||
|
(
|
||||||
|
"<!DOCTYPE html><html><head>"
|
||||||
|
f'<meta http-equiv="refresh" content="0; url={target}">'
|
||||||
|
f'<link rel="canonical" href="{target}">'
|
||||||
|
"</head><body>Redirecting...</body></html>"
|
||||||
|
),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
(SITE_DIR / ".nojekyll").touch()
|
||||||
|
|
||||||
|
|
||||||
|
def copy_tree(source_dir: Path, target_dir: Path) -> None:
|
||||||
|
if target_dir.exists():
|
||||||
|
shutil.rmtree(target_dir)
|
||||||
|
shutil.copytree(source_dir, target_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def resolve_source_dir(content_root: Path, language: str) -> Path:
|
||||||
|
candidates = [
|
||||||
|
content_root / "docs" / language,
|
||||||
|
content_root / "docs",
|
||||||
|
]
|
||||||
|
for candidate in candidates:
|
||||||
|
if candidate.exists() and ((candidate / "index.rst").exists() or (candidate / "index.md").exists()):
|
||||||
|
return candidate
|
||||||
|
|
||||||
|
fallback = REPO_ROOT / "docs" / language
|
||||||
|
if content_root != REPO_ROOT and fallback.exists():
|
||||||
|
print(
|
||||||
|
"WARNING: "
|
||||||
|
f"Falling back to current workspace docs for language '{language}' "
|
||||||
|
f"because {content_root} does not contain a buildable multilingual docs layout."
|
||||||
|
)
|
||||||
|
return fallback
|
||||||
|
|
||||||
|
raise RuntimeError(f"Missing documentation source directory for language '{language}' in {content_root}")
|
||||||
|
|
||||||
|
|
||||||
|
def build_language(version: dict, content_root: Path, language: str, output_dir: Path) -> None:
|
||||||
|
source_dir = resolve_source_dir(content_root, language)
|
||||||
|
conf_dir = DOCS_DIR / language
|
||||||
|
|
||||||
|
env = os.environ.copy()
|
||||||
|
env.update(
|
||||||
|
{
|
||||||
|
"DOCS_VERSION_SLUG": version["slug"],
|
||||||
|
"DOCS_VERSION_LABEL": version.get("label", version["slug"]),
|
||||||
|
"DOCS_VERSION_IS_LATEST": "1" if version.get("is_latest") else "0",
|
||||||
|
"DOCS_VERSION_MANIFEST_JSON": str(MANIFEST_PATH),
|
||||||
|
"DOCS_CONTENT_ROOT": str(source_dir),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
run(
|
||||||
|
[
|
||||||
|
"sphinx-build",
|
||||||
|
"-b",
|
||||||
|
"html",
|
||||||
|
"-c",
|
||||||
|
str(conf_dir),
|
||||||
|
str(source_dir),
|
||||||
|
str(output_dir),
|
||||||
|
],
|
||||||
|
env=env,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def build_version(version: dict) -> None:
|
||||||
|
version_root = get_content_root(version)
|
||||||
|
try:
|
||||||
|
for language in LANGUAGES:
|
||||||
|
temp_output_dir = WORKTREE_ROOT / ".build" / version["slug"] / language
|
||||||
|
if temp_output_dir.exists():
|
||||||
|
shutil.rmtree(temp_output_dir)
|
||||||
|
temp_output_dir.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
build_language(version, version_root, language, temp_output_dir)
|
||||||
|
|
||||||
|
if version.get("is_latest"):
|
||||||
|
target_dir = SITE_DIR / language
|
||||||
|
else:
|
||||||
|
target_dir = SITE_DIR / "versions" / version["slug"] / language
|
||||||
|
target_dir.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
copy_tree(temp_output_dir, target_dir)
|
||||||
|
finally:
|
||||||
|
cleanup_content_root(version_root)
|
||||||
|
|
||||||
|
|
||||||
|
def get_content_root(version: dict) -> Path:
|
||||||
|
if should_use_workspace(version):
|
||||||
|
return REPO_ROOT
|
||||||
|
|
||||||
|
ref = version.get("ref", "HEAD")
|
||||||
|
ensure_ref_available(ref)
|
||||||
|
resolved_ref = resolve_ref(ref)
|
||||||
|
|
||||||
|
worktree_dir = WORKTREE_ROOT / version["slug"]
|
||||||
|
if worktree_dir.exists():
|
||||||
|
run(["git", "worktree", "remove", "--force", str(worktree_dir)])
|
||||||
|
|
||||||
|
worktree_dir.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
run(["git", "worktree", "add", "--detach", str(worktree_dir), resolved_ref])
|
||||||
|
return worktree_dir
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup_content_root(content_root: Path) -> None:
|
||||||
|
if content_root == REPO_ROOT:
|
||||||
|
return
|
||||||
|
|
||||||
|
if content_root.exists():
|
||||||
|
run(["git", "worktree", "remove", "--force", str(content_root)])
|
||||||
|
|
||||||
|
|
||||||
|
def validate_manifest(manifest: dict) -> list[dict]:
|
||||||
|
published = [version for version in manifest.get("versions", []) if version.get("published")]
|
||||||
|
if not published:
|
||||||
|
raise RuntimeError("No published documentation versions found in manifest.")
|
||||||
|
if sum(1 for version in published if version.get("is_latest")) != 1:
|
||||||
|
raise RuntimeError("Manifest must contain exactly one published latest version.")
|
||||||
|
return published
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
manifest = load_manifest()
|
||||||
|
published_versions = validate_manifest(manifest)
|
||||||
|
|
||||||
|
prepare_site_dir()
|
||||||
|
WORKTREE_ROOT.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
for version in published_versions:
|
||||||
|
build_version(version)
|
||||||
|
|
||||||
|
create_root_redirect(manifest.get("default_language", "en"))
|
||||||
|
print(f"Built documentation site in {SITE_DIR}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
raise SystemExit(main())
|
||||||
|
except Exception as error: # noqa: BLE001
|
||||||
|
print(f"ERROR: {error}", file=sys.stderr)
|
||||||
|
raise
|
||||||
+20
-4
@@ -24,6 +24,15 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '_ext'))
|
|||||||
|
|
||||||
import recommonmark
|
import recommonmark
|
||||||
from recommonmark.transform import AutoStructify
|
from recommonmark.transform import AutoStructify
|
||||||
|
from versioning import (
|
||||||
|
build_version_context,
|
||||||
|
get_asset_path,
|
||||||
|
get_conf_root,
|
||||||
|
get_content_root,
|
||||||
|
get_extra_paths,
|
||||||
|
get_static_paths,
|
||||||
|
get_templates_paths,
|
||||||
|
)
|
||||||
|
|
||||||
# import sphinx_book_theme
|
# import sphinx_book_theme
|
||||||
# import sphinx_rtd_theme
|
# import sphinx_rtd_theme
|
||||||
@@ -35,6 +44,9 @@ from recommonmark.transform import AutoStructify
|
|||||||
html_theme = 'sphinx_rtd_theme'
|
html_theme = 'sphinx_rtd_theme'
|
||||||
# html_theme = "furo"
|
# html_theme = "furo"
|
||||||
|
|
||||||
|
content_root = get_content_root(__file__)
|
||||||
|
conf_root = get_conf_root(__file__)
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = 'OrbbecSDK V2 ROS2 Wrapper'
|
project = 'OrbbecSDK V2 ROS2 Wrapper'
|
||||||
@@ -57,6 +69,7 @@ extensions = ['recommonmark',
|
|||||||
'sphinx_copybutton',
|
'sphinx_copybutton',
|
||||||
'sphinx.ext.autosectionlabel',
|
'sphinx.ext.autosectionlabel',
|
||||||
'language_switch',
|
'language_switch',
|
||||||
|
'version_switch',
|
||||||
'section_search',
|
'section_search',
|
||||||
# 'myst_parser',
|
# 'myst_parser',
|
||||||
]
|
]
|
||||||
@@ -67,7 +80,7 @@ source_suffix = ['.rst', 'rest', '.md', '.MD']
|
|||||||
|
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = get_templates_paths(conf_root)
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
@@ -89,16 +102,16 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
|||||||
# html_favicon = "source/_static/orbbec_logo2.png"
|
# html_favicon = "source/_static/orbbec_logo2.png"
|
||||||
|
|
||||||
#html_logo = "source/_static/orbbec-light_no_colour.svg"
|
#html_logo = "source/_static/orbbec-light_no_colour.svg"
|
||||||
html_favicon = "source/_static/orbbec_footerlogo_3d_world.svg"
|
html_favicon = get_asset_path(conf_root, "source/_static/orbbec_footerlogo_3d_world.svg", "source/_static/orbbec_footerlogo_3d_world.svg")
|
||||||
# html_logo = "source/_static/orbbec_footerlogo_3d_world.svg"
|
# html_logo = "source/_static/orbbec_footerlogo_3d_world.svg"
|
||||||
# html_favicon = "source/_static/orbbec_footerlogo_3d_world.svg"
|
# html_favicon = "source/_static/orbbec_footerlogo_3d_world.svg"
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ['source/_static','source/image']
|
html_static_path = get_static_paths(conf_root, content_root)
|
||||||
|
|
||||||
html_extra_path = ['source/image']
|
html_extra_path = get_extra_paths(content_root)
|
||||||
|
|
||||||
html_theme_options = {
|
html_theme_options = {
|
||||||
# 'analytics_id': 'G-EVD5Z6G6NH',
|
# 'analytics_id': 'G-EVD5Z6G6NH',
|
||||||
@@ -137,6 +150,9 @@ html_theme_options = {
|
|||||||
|
|
||||||
# #不需要添加_static目录,加了会不起作用
|
# #不需要添加_static目录,加了会不起作用
|
||||||
html_css_files = ["custom.css"]
|
html_css_files = ["custom.css"]
|
||||||
|
html_context = {
|
||||||
|
"docs_versioning": build_version_context(language),
|
||||||
|
}
|
||||||
|
|
||||||
# 与文档无关的其他资源的路径,由于无需构建此文件夹的文件,会自动忽略
|
# 与文档无关的其他资源的路径,由于无需构建此文件夹的文件,会自动忽略
|
||||||
# 默认情况下,此路径下的文件会输出到生成的html的根目录
|
# 默认情况下,此路径下的文件会输出到生成的html的根目录
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
|
|
||||||
.lang-switch {
|
.lang-switch {
|
||||||
position: fixed !important;
|
position: static !important;
|
||||||
top: 60px !important;
|
display: flex !important;
|
||||||
right: 20px !important;
|
align-items: center !important;
|
||||||
z-index: 1000 !important;
|
gap: 0.45rem !important;
|
||||||
|
margin: 0 !important;
|
||||||
background: rgba(255,255,255,0.95) !important;
|
background: rgba(255,255,255,0.95) !important;
|
||||||
border: 1px solid #ddd !important;
|
border: 1px solid #ddd !important;
|
||||||
border-radius: 6px !important;
|
border-radius: 10px !important;
|
||||||
padding: 8px 12px !important;
|
padding: 8px 12px !important;
|
||||||
font-size: 0.9em !important;
|
font-size: 0.9em !important;
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.15) !important;
|
box-shadow: 0 8px 22px rgba(15, 23, 42, 0.14) !important;
|
||||||
backdrop-filter: blur(5px) !important;
|
backdrop-filter: blur(5px) !important;
|
||||||
transition: all 0.3s ease !important;
|
transition: all 0.3s ease !important;
|
||||||
}
|
}
|
||||||
@@ -26,7 +27,6 @@
|
|||||||
|
|
||||||
.lang-switch .lang-other {
|
.lang-switch .lang-other {
|
||||||
color: #0078d4 !important;
|
color: #0078d4 !important;
|
||||||
margin-right: 8px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lang-switch .lang-other:hover {
|
.lang-switch .lang-other:hover {
|
||||||
@@ -36,7 +36,6 @@
|
|||||||
.lang-switch .lang-current {
|
.lang-switch .lang-current {
|
||||||
color: #333 !important;
|
color: #333 !important;
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
margin-left: 8px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wy-nav-side .wy-side-nav-search {
|
.wy-nav-side .wy-side-nav-search {
|
||||||
@@ -121,8 +120,6 @@
|
|||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.lang-switch {
|
.lang-switch {
|
||||||
top: 10px !important;
|
|
||||||
right: 10px !important;
|
|
||||||
font-size: 0.8em !important;
|
font-size: 0.8em !important;
|
||||||
padding: 6px 8px !important;
|
padding: 6px 8px !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"default_language": "en",
|
||||||
|
"latest_slug": "Latest",
|
||||||
|
"versions": [
|
||||||
|
{
|
||||||
|
"slug": "Latest",
|
||||||
|
"label": "Latest",
|
||||||
|
"ref": "Document",
|
||||||
|
"published": true,
|
||||||
|
"is_latest": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "v2.8.4",
|
||||||
|
"label": "v2.8.4",
|
||||||
|
"ref": "docs/v2.8.4",
|
||||||
|
"published": true,
|
||||||
|
"is_latest": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slug": "v2.8.1",
|
||||||
|
"label": "v2.8.1",
|
||||||
|
"ref": "docs/v2.8.1",
|
||||||
|
"published": true,
|
||||||
|
"is_latest": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
+20
-4
@@ -24,6 +24,15 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '_ext'))
|
|||||||
|
|
||||||
import recommonmark
|
import recommonmark
|
||||||
from recommonmark.transform import AutoStructify
|
from recommonmark.transform import AutoStructify
|
||||||
|
from versioning import (
|
||||||
|
build_version_context,
|
||||||
|
get_asset_path,
|
||||||
|
get_conf_root,
|
||||||
|
get_content_root,
|
||||||
|
get_extra_paths,
|
||||||
|
get_static_paths,
|
||||||
|
get_templates_paths,
|
||||||
|
)
|
||||||
|
|
||||||
# import sphinx_book_theme
|
# import sphinx_book_theme
|
||||||
# import sphinx_rtd_theme
|
# import sphinx_rtd_theme
|
||||||
@@ -35,6 +44,9 @@ from recommonmark.transform import AutoStructify
|
|||||||
html_theme = 'sphinx_rtd_theme'
|
html_theme = 'sphinx_rtd_theme'
|
||||||
# html_theme = "furo"
|
# html_theme = "furo"
|
||||||
|
|
||||||
|
content_root = get_content_root(__file__)
|
||||||
|
conf_root = get_conf_root(__file__)
|
||||||
|
|
||||||
# -- Project information -----------------------------------------------------
|
# -- Project information -----------------------------------------------------
|
||||||
|
|
||||||
project = 'OrbbecSDK V2 ROS2 封装'
|
project = 'OrbbecSDK V2 ROS2 封装'
|
||||||
@@ -57,6 +69,7 @@ extensions = ['recommonmark',
|
|||||||
'sphinx_copybutton',
|
'sphinx_copybutton',
|
||||||
'sphinx.ext.autosectionlabel',
|
'sphinx.ext.autosectionlabel',
|
||||||
'language_switch',
|
'language_switch',
|
||||||
|
'version_switch',
|
||||||
'section_search',
|
'section_search',
|
||||||
# 'myst_parser',
|
# 'myst_parser',
|
||||||
]
|
]
|
||||||
@@ -67,7 +80,7 @@ source_suffix = ['.rst', 'rest', '.md', '.MD']
|
|||||||
|
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = get_templates_paths(conf_root)
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
@@ -88,16 +101,16 @@ exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
|||||||
# html_favicon = "source/_static/orbbec_logo2.png"
|
# html_favicon = "source/_static/orbbec_logo2.png"
|
||||||
|
|
||||||
#html_logo = "source/_static/orbbec-light_no_colour.svg"
|
#html_logo = "source/_static/orbbec-light_no_colour.svg"
|
||||||
html_favicon = "source/_static/orbbec_footerlogo_3d_world.svg"
|
html_favicon = get_asset_path(conf_root, "source/_static/orbbec_footerlogo_3d_world.svg", "source/_static/orbbec_footerlogo_3d_world.svg")
|
||||||
# html_logo = "source/_static/orbbec_footerlogo_3d_world.svg"
|
# html_logo = "source/_static/orbbec_footerlogo_3d_world.svg"
|
||||||
# html_favicon = "source/_static/orbbec_footerlogo_3d_world.svg"
|
# html_favicon = "source/_static/orbbec_footerlogo_3d_world.svg"
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ['source/_static','source/image']
|
html_static_path = get_static_paths(conf_root, content_root)
|
||||||
|
|
||||||
html_extra_path = ['source/image']
|
html_extra_path = get_extra_paths(content_root)
|
||||||
|
|
||||||
html_theme_options = {
|
html_theme_options = {
|
||||||
# 'analytics_id': 'G-EVD5Z6G6NH',
|
# 'analytics_id': 'G-EVD5Z6G6NH',
|
||||||
@@ -135,6 +148,9 @@ html_theme_options = {
|
|||||||
|
|
||||||
# #不需要添加_static目录,加了会不起作用
|
# #不需要添加_static目录,加了会不起作用
|
||||||
html_css_files = ["custom.css"]
|
html_css_files = ["custom.css"]
|
||||||
|
html_context = {
|
||||||
|
"docs_versioning": build_version_context(language),
|
||||||
|
}
|
||||||
|
|
||||||
# 与文档无关的其他资源的路径,由于无需构建此文件夹的文件,会自动忽略
|
# 与文档无关的其他资源的路径,由于无需构建此文件夹的文件,会自动忽略
|
||||||
# 默认情况下,此路径下的文件会输出到生成的html的根目录
|
# 默认情况下,此路径下的文件会输出到生成的html的根目录
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
|
|
||||||
.lang-switch {
|
.lang-switch {
|
||||||
position: fixed !important;
|
position: static !important;
|
||||||
top: 60px !important;
|
display: flex !important;
|
||||||
right: 20px !important;
|
align-items: center !important;
|
||||||
z-index: 1000 !important;
|
gap: 0.45rem !important;
|
||||||
|
margin: 0 !important;
|
||||||
background: rgba(255,255,255,0.95) !important;
|
background: rgba(255,255,255,0.95) !important;
|
||||||
border: 1px solid #ddd !important;
|
border: 1px solid #ddd !important;
|
||||||
border-radius: 6px !important;
|
border-radius: 10px !important;
|
||||||
padding: 8px 12px !important;
|
padding: 8px 12px !important;
|
||||||
font-size: 0.9em !important;
|
font-size: 0.9em !important;
|
||||||
box-shadow: 0 2px 8px rgba(0,0,0,0.15) !important;
|
box-shadow: 0 8px 22px rgba(15, 23, 42, 0.14) !important;
|
||||||
backdrop-filter: blur(5px) !important;
|
backdrop-filter: blur(5px) !important;
|
||||||
transition: all 0.3s ease !important;
|
transition: all 0.3s ease !important;
|
||||||
}
|
}
|
||||||
@@ -26,7 +27,6 @@
|
|||||||
|
|
||||||
.lang-switch .lang-other {
|
.lang-switch .lang-other {
|
||||||
color: #0078d4 !important;
|
color: #0078d4 !important;
|
||||||
margin-right: 8px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.lang-switch .lang-other:hover {
|
.lang-switch .lang-other:hover {
|
||||||
@@ -36,7 +36,6 @@
|
|||||||
.lang-switch .lang-current {
|
.lang-switch .lang-current {
|
||||||
color: #333 !important;
|
color: #333 !important;
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
margin-left: 8px !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wy-nav-side .wy-side-nav-search {
|
.wy-nav-side .wy-side-nav-search {
|
||||||
@@ -121,8 +120,6 @@
|
|||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.lang-switch {
|
.lang-switch {
|
||||||
top: 10px !important;
|
|
||||||
right: 10px !important;
|
|
||||||
font-size: 0.8em !important;
|
font-size: 0.8em !important;
|
||||||
padding: 6px 8px !important;
|
padding: 6px 8px !important;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user