Metadata-Version: 2.4
Name: sphinx-autoopengraph
Version: 0.1.0
Summary: Automatic per-page Open Graph image and description selection for Sphinx, on top of sphinxext-opengraph.
Author-email: The PyVista Developers <info@pyvista.org>
License-Expression: MIT
Project-URL: Homepage, https://github.com/user27182/sphinx-autoopengraph
Keywords: documentation,opengraph,sphinx
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: Sphinx :: Extension
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Documentation :: Sphinx
Classifier: Topic :: Software Development :: Documentation
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: sphinx>=7
Dynamic: license-file

# sphinx-autoopengraph

Automatic, per-page [Open Graph](https://ogp.me) link previews for Sphinx
documentation, built on top of
[sphinxext-opengraph](https://github.com/wpilibsuite/sphinxext-opengraph).

When someone shares a link to your documentation -- on social media, in a chat
app, anywhere that unfurls links -- the preview card that appears is built from
the page's Open Graph metadata. `sphinx-autoopengraph` fills that metadata in
for you, so every page previews with an image it actually shows and a
description written from its own opening prose, instead of the site-wide
defaults you would otherwise get on every page alike.

## Why this extension?

`sphinxext-opengraph` writes the `og:image` / `og:description` tags, but by
default every page gets the same one site-wide image, and a description built
by walking the page's text nodes until it has enough characters.

`sphinxext-opengraph` does have `ogp_use_first_image`, which gets partway
there: it selects the first `docutils` image node it finds. The image half of
this extension does that too, but goes further -- it lets you *choose which*
image by number, rather than only ever taking the first, and if the page is a
[Sphinx-Gallery](https://sphinx-gallery.github.io) example, it matches
whichever image the gallery itself picked as its thumbnail instead, so a
shared link and the gallery agree.

Image selection is not tied to any one plot-generating extension -- it counts
every image node on the page, in document order, whatever produced it. That
means it works out of the box with a page that renders images via matplotlib's
own [`.. plot::`](https://matplotlib.org/stable/api/sphinxext_plot_directive_api.html)
directive, [PyVista](https://github.com/pyvista/pyvista)'s
`.. pyvista-plot::`, a plain hand-written `.. image::`, or any mix of
these on the same page.

The description half has no equivalent in `sphinxext-opengraph` at all, and
matters most for exactly the two page shapes a documentation site built with
Sphinx-Gallery and single-page-per-object API references (the combination
[PyVista](https://github.com/pyvista/pyvista) uses, where this extension
started) tends to be made of almost entirely:

- **API reference pages.** Sphinx wraps autodoc output in a node that
  subclasses `docutils.nodes.Admonition`, and `sphinxext-opengraph`'s
  walker skips every admonition. That makes every docstring on the page
  invisible to it -- the description it picks falls back to whatever text
  happens to sit outside that node, or nothing at all.
- **Sphinx-Gallery examples.** The walker has no notion of gallery furniture,
  so it picks up download links, the timing footer, and the "Gallery
  generated by Sphinx-Gallery" signature line.

This extension collects whole paragraphs of real prose instead, in document
order, skipping the structural furniture (signatures, parameter tables, code
blocks, admonitions, download links, captions, navigation, ...) that trips up
a plain text walk. For a docstring that means its summary and the paragraphs
that follow it; for a gallery example it means the example's own introduction.

## Installation

```bash
pip install sphinx-autoopengraph
```

Add both extensions to your Sphinx `conf.py` and tell `sphinxext-opengraph`
where your documentation is published:

```python
extensions = [
    ...,
    "sphinx_autoopengraph",
    "sphinxext.opengraph",
]

ogp_site_url = "https://docs.example.org/"
ogp_image = "https://docs.example.org/_static/social_preview.png"
```

Both extensions are required: `sphinx_autoopengraph` is what chooses each
page's image and description, and `sphinxext-opengraph` is what writes the
tags -- without it, `sphinx_autoopengraph` does nothing. Listing both
extensions is itself the opt-in. A page's preview image is chosen from
whatever images it has, regardless of source. Its description is built from
its own prose, whether or not the page has any images at all.

Setting `ogp_image` is optional but recommended: it is `sphinxext-opengraph`'s
own site-wide default, and this extension only ever overrides it for a page
that has an image of its own. A page with none -- most hand-written prose
pages -- keeps `ogp_image` as its preview, so without it those pages have no
preview image at all.

Either half can be turned off on its own:

```python
autoopengraph_image = False
autoopengraph_description = False
```

Both default to `True`.

## Choosing the preview image

By default a page previews the first image it shows. Pick a different one
with the `autoopengraph_thumbnail` directive:

```rst
.. autoopengraph_thumbnail:: 2
```

The argument is the one-based position of the image among *all* images on the
page, in the order they appear. It counts images, not files, so it is
unaffected by how generated filenames happen to be numbered. Negative values
count backwards from the last image.

The directive renders nothing and can go anywhere on the page, so you can put
it next to the code it refers to rather than at the top. In a docstring, the
natural place is the start of the `Examples` section:

```rst
Examples
--------
.. autoopengraph_thumbnail:: 2

Create a sphere.

>>> import pyvista as pv
>>> pv.Sphere().plot()

Clip it, which is what this page is really about.

>>> pv.Sphere().clip().plot()
```

Two things to be aware of when using it:

- A page has a single `<head>`, so it gets a single link preview -- section
  anchors cannot have their own. Using the directive twice on one page warns
  and keeps the first selection. This can happen without either docstring
  being wrong, on pages that document several objects at once (e.g. Sphinx's
  own `:members:`).
- Selecting an image the page does not have also warns, and falls back to the
  first image.

Pages with no images at all keep whatever site-wide `ogp_image` you have
configured.

### Sphinx-Gallery examples

Gallery examples already have a thumbnail, and their preview always matches
it, so a shared link shows the same picture as the gallery. The full
resolution version of that image is used rather than the gallery's own
thumbnail file, which is too small to preview well.

Using `autoopengraph_thumbnail` in a gallery example is an error. Select the
image with Sphinx-Gallery's own comment instead:

```python
# sphinx_gallery_thumbnail_number = 2
```

## Preview descriptions

Each page is described by its leading paragraphs of real prose, up to
`ogp_description_length` characters, skipping signatures, parameter tables,
code blocks, admonitions, download links, captions and navigation. For a
docstring that is its summary and the paragraphs following it; for a gallery
example it is the example's introduction. The plain `description` meta tag is
set to match, unless `ogp_enable_meta_description` is disabled.

## Development

```bash
uv sync --group dev
uv run pytest
uv run pre-commit run --all-files
```
