docs: implement versioning system and enhance documentation structure

This commit is contained in:
ob-yalian
2026-04-24 15:48:54 +08:00
parent a487e67e1c
commit 91e1a978e8
14 changed files with 845 additions and 45 deletions
+69
View File
@@ -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 -2
View File
@@ -15,22 +15,35 @@ def inject_language_switch(app, doctree, docname):
other_label = '中文' if is_en else 'English'
current_label = 'English' if is_en else '中文'
# JS now detects /en/ or /zh/ segment and swaps only the first occurrence
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-current" href="#">{current_label}</a>'
'</div>'
'<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 isEn=p.indexOf('/en/')!==-1 && p.indexOf('/zh/')===-1;"
"if(p.indexOf('/zh/')!==-1) isEn=false;"
"var other = isEn ? p.replace(/\\/en\\//, '/zh/') : p.replace(/\\/zh\\//, '/en/');"
"var current=p;"
"var sw=document.querySelector('.lang-switch'); if(!sw) return;"
"ensureDock().appendChild(sw);"
"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='中文'; }"
"o.setAttribute('href', other); c.setAttribute('href', current);"
"}"
"if(document.body){ mountSwitch(); }"
"else { document.addEventListener('DOMContentLoaded', mountSwitch, {once:true}); }"
'})();</script>'
)
doctree.insert(0, nodes.raw('', html, format='html'))
+96
View File
@@ -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;
}
}
+155
View File
@@ -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;
});
});
});
})();
+49
View File
@@ -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"))
+102
View File
@@ -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,
}
+268
View File
@@ -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
View File
@@ -24,6 +24,15 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '_ext'))
import recommonmark
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_rtd_theme
@@ -35,6 +44,9 @@ from recommonmark.transform import AutoStructify
html_theme = 'sphinx_rtd_theme'
# html_theme = "furo"
content_root = get_content_root(__file__)
conf_root = get_conf_root(__file__)
# -- Project information -----------------------------------------------------
project = 'OrbbecSDK V2 ROS2 Wrapper'
@@ -57,6 +69,7 @@ extensions = ['recommonmark',
'sphinx_copybutton',
'sphinx.ext.autosectionlabel',
'language_switch',
'version_switch',
'section_search',
# 'myst_parser',
]
@@ -67,7 +80,7 @@ source_suffix = ['.rst', 'rest', '.md', '.MD']
# 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
# 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_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_favicon = "source/_static/orbbec_footerlogo_3d_world.svg"
# 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,
# 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 = {
# 'analytics_id': 'G-EVD5Z6G6NH',
@@ -137,6 +150,9 @@ html_theme_options = {
# #不需要添加_static目录,加了会不起作用
html_css_files = ["custom.css"]
html_context = {
"docs_versioning": build_version_context(language),
}
# 与文档无关的其他资源的路径,由于无需构建此文件夹的文件,会自动忽略
# 默认情况下,此路径下的文件会输出到生成的html的根目录
+9 -12
View File
@@ -1,15 +1,16 @@
.lang-switch {
position: fixed !important;
top: 60px !important;
right: 20px !important;
z-index: 1000 !important;
position: static !important;
display: flex !important;
align-items: center !important;
gap: 0.45rem !important;
margin: 0 !important;
background: rgba(255,255,255,0.95) !important;
border: 1px solid #ddd !important;
border-radius: 6px !important;
border-radius: 10px !important;
padding: 8px 12px !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;
transition: all 0.3s ease !important;
}
@@ -26,7 +27,6 @@
.lang-switch .lang-other {
color: #0078d4 !important;
margin-right: 8px !important;
}
.lang-switch .lang-other:hover {
@@ -36,7 +36,6 @@
.lang-switch .lang-current {
color: #333 !important;
font-weight: bold !important;
margin-left: 8px !important;
}
.wy-nav-side .wy-side-nav-search {
@@ -121,10 +120,8 @@
@media (max-width: 768px) {
.lang-switch {
top: 10px !important;
right: 10px !important;
font-size: 0.8em !important;
padding: 6px 8px !important;
font-size: 0.8em !important;
padding: 6px 8px !important;
}
}
+27
View File
@@ -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
View File
@@ -24,6 +24,15 @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '_ext'))
import recommonmark
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_rtd_theme
@@ -35,6 +44,9 @@ from recommonmark.transform import AutoStructify
html_theme = 'sphinx_rtd_theme'
# html_theme = "furo"
content_root = get_content_root(__file__)
conf_root = get_conf_root(__file__)
# -- Project information -----------------------------------------------------
project = 'OrbbecSDK V2 ROS2 封装'
@@ -57,6 +69,7 @@ extensions = ['recommonmark',
'sphinx_copybutton',
'sphinx.ext.autosectionlabel',
'language_switch',
'version_switch',
'section_search',
# 'myst_parser',
]
@@ -67,7 +80,7 @@ source_suffix = ['.rst', 'rest', '.md', '.MD']
# 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
# 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_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_favicon = "source/_static/orbbec_footerlogo_3d_world.svg"
# 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,
# 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 = {
# 'analytics_id': 'G-EVD5Z6G6NH',
@@ -135,6 +148,9 @@ html_theme_options = {
# #不需要添加_static目录,加了会不起作用
html_css_files = ["custom.css"]
html_context = {
"docs_versioning": build_version_context(language),
}
# 与文档无关的其他资源的路径,由于无需构建此文件夹的文件,会自动忽略
# 默认情况下,此路径下的文件会输出到生成的html的根目录
+9 -12
View File
@@ -1,15 +1,16 @@
.lang-switch {
position: fixed !important;
top: 60px !important;
right: 20px !important;
z-index: 1000 !important;
position: static !important;
display: flex !important;
align-items: center !important;
gap: 0.45rem !important;
margin: 0 !important;
background: rgba(255,255,255,0.95) !important;
border: 1px solid #ddd !important;
border-radius: 6px !important;
border-radius: 10px !important;
padding: 8px 12px !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;
transition: all 0.3s ease !important;
}
@@ -26,7 +27,6 @@
.lang-switch .lang-other {
color: #0078d4 !important;
margin-right: 8px !important;
}
.lang-switch .lang-other:hover {
@@ -36,7 +36,6 @@
.lang-switch .lang-current {
color: #333 !important;
font-weight: bold !important;
margin-left: 8px !important;
}
.wy-nav-side .wy-side-nav-search {
@@ -121,10 +120,8 @@
@media (max-width: 768px) {
.lang-switch {
top: 10px !important;
right: 10px !important;
font-size: 0.8em !important;
padding: 6px 8px !important;
font-size: 0.8em !important;
padding: 6px 8px !important;
}
}