Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
86e81af
Test
kacperbojakowski-3di Apr 27, 2026
929e4fd
Fix
kacperbojakowski-3di Apr 27, 2026
3d437e4
OPENR-89: Simple test for adding an enhance job and run script on cha…
keithkirkwood-3di May 1, 2026
7351701
OPENR-89: Return the test workflow to original state
keithkirkwood-3di May 4, 2026
20efaf8
OPENR-89: Use new workflow based on push. List out files which remain…
keithkirkwood-3di May 4, 2026
7377972
OPENR-89: Fix to fetch history so that SHAs exist
keithkirkwood-3di May 4, 2026
39eb9ac
OPENR-89: Test script in wrong location
keithkirkwood-3di May 4, 2026
292e455
OPENR-89: Creat RST-specific parsing and updates in new module, and u…
keithkirkwood-3di May 4, 2026
6fd04f8
OPENR-89: Fix bug using base filename for enhance results data
keithkirkwood-3di May 4, 2026
93734f4
OPENR-89: Install python dependencies in workflow
keithkirkwood-3di May 5, 2026
88bf36a
OPENR-89: Prompt adjustments
keithkirkwood-3di May 5, 2026
2b8021e
OPENR-89: Only make API calls for fields which do not exist
keithkirkwood-3di May 5, 2026
9badeb9
OPENR-89: Enhance only RST files
keithkirkwood-3di May 5, 2026
0ededa3
OPENR-89: Small tweaks to logging and constants
keithkirkwood-3di May 5, 2026
80eb658
Fix
kacperbojakowski-3di May 6, 2026
2900ddd
Add proxy logic
kacperbojakowski-3di May 6, 2026
e6389f8
OPENR-89: Adding some sanity check validation for generated values - …
keithkirkwood-3di May 6, 2026
b0513cd
OPENR-89: Add some unit tests for central enhance topics module
keithkirkwood-3di May 6, 2026
c16efda
Add introduction
kacperbojakowski-3di May 6, 2026
e86ac48
Add related articles
kacperbojakowski-3di May 7, 2026
15e7185
Remove custom CSS
kacperbojakowski-3di May 7, 2026
24a4336
Proxy fix
kacperbojakowski-3di May 7, 2026
a75df30
OPENR-89: Add short description custom directive
keithkirkwood-3di May 15, 2026
f506799
add pagefind from prototype
May 18, 2026
25a2cff
Revert "add pagefind from prototype"
May 18, 2026
c9b316e
OPENR-89: First attempt at short desc enhancement
keithkirkwood-3di May 18, 2026
795f1a0
update RT Corporation url to fix build
3di-techx May 19, 2026
e504a35
Fix proxy
kacperbojakowski-3di May 20, 2026
8f1c64c
Update related_packages.js
kacperbojakowski-3di May 20, 2026
5886634
add page find
3di-techx May 20, 2026
55716fd
OPENR-89: Next iteration of short description enhancement
keithkirkwood-3di May 20, 2026
2bfb3c4
url fix
3di-techx May 20, 2026
ec78b29
Merge branch 'ros2:rolling' into enhance-content
keithkirkwood-3di May 20, 2026
c9e41f8
OPENR-89: Update example topics to use short desc
keithkirkwood-3di May 20, 2026
6689a6d
search page url fix
3di-techx May 20, 2026
3ed6130
OPENR-89: Fixed short description orchestration to use (non-deprecate…
keithkirkwood-3di May 20, 2026
cf77237
Update README.md
3di-techx May 20, 2026
ac96145
OPENR-89: Roll out the Responses API and proper file uploads to unify…
keithkirkwood-3di May 20, 2026
bfa47e5
OPENR-89: Add fixed metadata for distro and product, fix vector store…
keithkirkwood-3di May 20, 2026
d9bc4ad
Final fix
kacperbojakowski-3di May 21, 2026
c139986
Set default rosdistro cache proxy path in conf.py
kacperbojakowski-3di May 21, 2026
4a7bce9
Bring topic up to date for consistency
kacperbojakowski-3di May 21, 2026
bae9756
Merge branch '3di-ros-poc' into dynamic-lists
kacperbojakowski-3di May 21, 2026
1e700ee
Merge branch 'ros2:rolling' into enhance-content
keithkirkwood-3di May 21, 2026
f205c67
OPENR-89: Remove new GitHub workflow for now
keithkirkwood-3di May 21, 2026
f6bac23
Merge branch 'ros2:rolling' into 3di-ros-poc
3di-techx May 21, 2026
9cdfcb4
Merge branch '3di-ros-poc' into enhance-content
keithkirkwood-3di May 21, 2026
8e0f387
OPENR-89: Fix trailinig whitespace in topic
keithkirkwood-3di May 21, 2026
395a836
Merge branch 'enhance-content' of https://github.com/keithkirkwood-3d…
keithkirkwood-3di May 21, 2026
1bd2669
Merge pull request #7 from keithkirkwood-3di/enhance-content
3di-techx May 21, 2026
fa40018
Merge branch '3di-ros-poc' into dynamic-lists
3di-techx May 21, 2026
1453a59
Merge pull request #4 from kacperbojakowski-3di/dynamic-lists
3di-techx May 21, 2026
4c5b415
Delete .github/workflows/enhance.yml
3di-techx May 21, 2026
4e4c8ee
Merge branch '3di-ros-poc' into add-pagefind-search
3di-techx May 21, 2026
97d081d
Merge pull request #3 from 3di-techx/add-pagefind-search
3di-techx May 21, 2026
220a1e2
OPENR-89: Reinstate extensions taken out by merge
keithkirkwood-3di May 21, 2026
15de1d9
OPENR-89: Fix paths for pagefind
keithkirkwood-3di May 22, 2026
6718a24
OPENR-89: Move enhance to tools folder
keithkirkwood-3di May 22, 2026
952da07
OPENR-89: Update metadata on topics for demo
keithkirkwood-3di May 22, 2026
f4efddb
OPENR-89: Add related articles directive to 2 further topics for demos
keithkirkwood-3di May 22, 2026
c0c0e0a
OPENR: Change distro to distribution meta name
keithkirkwood-3di May 22, 2026
02661c3
add config to define meta used for search resilts filtering
May 22, 2026
41975b5
apply config to search filters
May 22, 2026
e4e3cc1
Revert "apply config to search filters"
May 22, 2026
54cd2cf
refine facet list
May 22, 2026
69314f5
tidy file
3di-techx May 22, 2026
9f363e8
OPENR-89: Update script to distribution also, and makefile target to …
keithkirkwood-3di May 26, 2026
068763b
Merge branch '3di-ros-poc' of https://github.com/3di-techx/ros2_docum…
keithkirkwood-3di May 26, 2026
83dc783
OPENR-89: Resolve key mismatch
keithkirkwood-3di May 26, 2026
5433468
OPENR-98: Changes to README, now targeted as dev docs.
keithkirkwood-3di Jun 2, 2026
19a807d
OPENR-98: Further updates to README
keithkirkwood-3di Jun 3, 2026
21cf17e
Merge branch 'rolling' into 3di-ros-poc
kscottz Jun 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,15 @@ jobs:
- name: Build the docs
run: make html


- name: Setup Node.js (Pagefind)
uses: actions/setup-node@v4
with:
node-version: '24'

- name: Index HTML with Pagefind
run: make pagefind

- name: Upload document artifacts
uses: actions/upload-artifact@v4
id: artifact-upload-step
Expand Down Expand Up @@ -147,3 +156,12 @@ jobs:

- name: Build the docs
run: make multiversion


- name: Setup Node.js (Pagefind)
uses: actions/setup-node@v4
with:
node-version: '24'

- name: Index HTML with Pagefind
run: make pagefind
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,7 @@ _build/
__pycache__
ros2doc/
.DS_Store

# Downloaded at HTML build time for browser-side package lists (large).
source/_static/rosdistro_cache/*.yaml.gz
.env
19 changes: 18 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,26 @@ multiversion: Makefile
@echo "<html><head><meta http-equiv=\"refresh\" content=\"0; url=lyrical/index.html\" /></head></html>" > build/html/index.html
$(PYTHON) make_sitemapindex.py

# Pagefind static search index (requires Node.js / npx). Run after html or multiversion.
PAGEFIND_VERSION ?= 1.5.2
pagefind:
npx -y pagefind@$(PAGEFIND_VERSION) --site "$(OUT)/html"


# Convenience: Sphinx build + Pagefind index (does not replace plain html / multiversion).
html-search:
$(MAKE) html
$(MAKE) pagefind

multiversion-search: multiversion
$(MAKE) pagefind

%: Makefile
@$(BUILD) -M $@ "$(SOURCE)" "$(OUT)" $(OPTS)

enhance:
git diff --name-only --diff-filter=d HEAD | xargs -r $(PYTHON) tools/enhance_topics.py

lint:
./sphinx-lint-with-ros source

Expand Down Expand Up @@ -66,4 +83,4 @@ linkcheck:
serve:
sphinx-autobuild --host $(LIVE_HOST) --port $(LIVE_PORT) -c . $(SOURCE) $(OUT)/html

.PHONY: help Makefile multiversion test test-tools linkcheck serve lint spellcheck check-dictionaries sort-dictionaries
.PHONY: help Makefile multiversion pagefind test test-tools linkcheck serve lint spellcheck check-dictionaries sort-dictionaries
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,24 @@ To test building the multisite version deployed to the website use:

**NB:** This will ignore local workspace changes and build from the branches.

### Pagefind search index

After `make html` or `make multiversion`, run [Pagefind](https://pagefind.app/) so the built HTML under `build/html` is indexed and `build/html/pagefind/` is written (search bundle and Component UI assets). From the repo root:

`make pagefind`

Or use convenience targets that run Sphinx and Pagefind in one step:

- `make html-search` — `make html` then `make pagefind`
- `make multiversion-search` — `make multiversion` then `make pagefind`

Plain `make html` and `make multiversion` do **not** run Pagefind (Node.js is only required when you index search).

This requires **Node.js** (for `npx`). Pin the CLI with `PAGEFIND_VERSION` in the Makefile if needed.

The production [Jenkins doc job](https://build.ros.org/job/doc_ros2doc) should run the same `pagefind` step on `build/html` after Sphinx so deployed pages include the search bundle.


### Note for Windows (WSL) Users

When building the documentation on windows using WSL, it is recommended to clone and work with this repository inside the Linux filesystem (for example, under `/home/<user>/`) rather than under `/mnt/c`.
Expand Down
82 changes: 80 additions & 2 deletions conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,41 @@
'sphinx_adopters',
'sphinxcontrib.googleanalytics',
'sphinxcontrib.mermaid',
'ros_related_packages',
'ros_related_articles',
'short_description',
'pagefind_meta',
'showmeta',
]

# pagefind search index configuration.

pagefind_merge_enabled = False
pagefind_merge_package_pkgs = []
pagefind_merge_index_base = 'https://docs.ros.org'
pagefind_merge_index_overrides = {}
pagefind_merge_filter_per_pkg = None
pagefind_merge_index_weight_per_pkg = None

# Pagefind search UI (modal + /search.html): result metadata lines and facet sidebar.
# Dict keys = .. meta:: field names; values = display labels.
# Order here is facet dropdown order and result-meta line order (allowlist).
# Only listed keys are indexed as facets; keys must exist on at least one page in the build.
# Other meta (e.g. description, keywords) stays SEO-only and does not appear in the facet sidebar.

pagefind_result_meta_order = {
'product': 'Product',
'distribution': 'Distribution',
'area': 'Area',
'capability': 'Capability',
'community': 'Community',
'installation': 'Installation',
'framework': 'Framework',
'tool': 'Tools',
'contentType': 'Content type',
'experience': 'Level',
}

# Intersphinx mapping

intersphinx_mapping = {
Expand Down Expand Up @@ -192,6 +225,7 @@
'DISTRO_UBUNTU_DEB_PLATFORM': distro_ubuntu_deb_platform['rolling'],
'DISTRO_ARM_STATUS_SUFFIX': distro_arm_status_suffix.get('rolling', 'unv8'),
'REPOS_FILE_BRANCH': 'rolling',
'PRODUCT': 'ROS 2',
}

html_favicon = 'favicon.ico'
Expand All @@ -205,8 +239,52 @@
html_sourcelink_suffix = ''

# Relative to html_static_path
html_css_files = ['custom.css', 'adopters.css']
html_js_files = ['adopters.js']
html_css_files = ['custom.css', 'adopters.css', 'pagefind-docsearch.css']
html_js_files = [
('vendor/pako.min.js', {'defer': ''}),
('vendor/js-yaml.min.js', {'defer': ''}),
'adopters.js',
'related_packages.js',
]

# Runtime proxy endpoint for freshest rosdistro cache data (same-origin).
# Default matches production: /api/rosdistro-cache/{distro}-cache.yaml.gz
# Override with ROS_RELATED_PACKAGES_PROXY_URL; set to empty string to disable
# proxy and use bundled _static fallback only.
# Local testing: python tools/serve_docs_with_proxy.py (serves build/html + /api/).
def _normalize_ros_related_packages_proxy_url(raw: str) -> str:
"""Return a browser-safe proxy template.

On Windows, GNU make / MSYS (common even when the terminal is PowerShell) can
rewrite ``/api/...`` into ``C:/Program Files/Git/api/...``. Recover the
intended same-origin path when that happens.
"""
value = (raw or '').strip()
if not value:
return ''

normalized = value.replace('\\', '/')
marker = '/api/rosdistro-cache/'
idx = normalized.find(marker)
if idx != -1:
return normalized[idx:]

if normalized.startswith('api/rosdistro-cache/'):
return '/' + normalized

return value


_DEFAULT_ROS_RELATED_PACKAGES_PROXY_URL = (
'/api/rosdistro-cache/{distro}-cache.yaml.gz'
)

ros_related_packages_proxy_url = _normalize_ros_related_packages_proxy_url(
os.environ.get(
'ROS_RELATED_PACKAGES_PROXY_URL',
_DEFAULT_ROS_RELATED_PACKAGES_PROXY_URL,
)
)

# -- Options for HTMLHelp output ------------------------------------------

Expand Down
4 changes: 4 additions & 0 deletions constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ imagesize==1.4.1
iniconfig==2.1.0
Jinja2==3.1.6
MarkupSafe==3.0.3
openai==2.33.0
packaging==25.0
pluggy==1.6.0
polib==1.2.0
Pygments==2.19.2
pytest==8.4.2
python-dotenv==1.1.0
PyYAML==6.0.3
regex==2025.9.18
requests==2.32.5
Expand All @@ -39,4 +41,6 @@ sphinxcontrib-mermaid==1.0.0
sphinxcontrib-qthelp==2.0.0
sphinxcontrib-serializinghtml==2.0.0
stevedore==5.5.0
tenacity==9.1.4
timeout-decorator==0.5.0
urllib3==2.5.0
70 changes: 70 additions & 0 deletions plugins/meta_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Copyright 2026 Open Robotics — shared helpers for ``.. meta::`` / Pagefind
"""
Collect every ``.. meta::`` field from the doctree, sanitize keys, and expand
``{MACRO}`` placeholders using the Sphinx ``macros`` config (longest keys first).

Sphinx / the HTML theme may also emit plain ``<meta>`` tags for the same fields.
The Pagefind extension emits additional tags with ``data-pagefind-filter`` and may
split comma-separated values into multiple tags for faceted search.
"""

from __future__ import annotations

import re
from typing import Dict, List, Optional

from docutils import nodes

# HTML ``<meta name="...">`` names should be conservative; allow common patterns.
_META_NAME_RE = re.compile(r'^[A-Za-z0-9_.:-]+$')


def sanitize_meta_key(raw: str) -> Optional[str]:
s = str(raw).strip()
if not s or not _META_NAME_RE.match(s):
return None
return s


def all_doctree_meta(doctree: Optional[nodes.document]) -> Dict[str, str]:
"""Return last-wins mapping of every ``nodes.meta`` ``name``/``property`` → ``content``."""
if doctree is None:
return {}

out: Dict[str, str] = {}
for meta in doctree.findall(nodes.meta):
if meta.get('http-equiv'):
continue
content = meta.get('content')
if not content:
continue
key: Optional[str] = None
name = meta.get('name')
if name:
key = sanitize_meta_key(str(name))
else:
prop = meta.get('property')
if prop:
key = sanitize_meta_key(str(prop))
if not key:
continue
out[key] = str(content).strip()
return out


def expand_meta_macros(text: str, macros: Dict[str, str]) -> str:
"""Expand ``{KEY}`` placeholders; longer macro names first to avoid partial matches."""
result = text
for key, value in sorted(macros.items(), key=lambda kv: len(kv[0]), reverse=True):
result = result.replace(f'{{{key}}}', value)
return result


def expand_all_meta_values(meta: Dict[str, str], macros: Dict[str, str]) -> Dict[str, str]:
"""Apply ``expand_meta_macros`` to every meta value."""
return {k: expand_meta_macros(v, macros) for k, v in meta.items()}


def split_meta_values(value: str) -> List[str]:
"""Return comma-separated metadata values as individual Pagefind values."""
return [part.strip() for part in value.split(',') if part.strip()]
Loading
Loading