Metadata-Version: 2.4
Name: sphinx-autoopengraph
Version: 0.2.1
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 *locally rendered* 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's [`.. pyvista-plot::`](https://dev.pyvista.org/extras/plot_directive)
directive, a plain hand-written `.. image::`, or any mix of these on the same
page. An externally hosted `.. image::` (any URL with a scheme, e.g. `https://...`)
is never a candidate -- in practice these are almost always a CI status badge, a
PyPI/conda version shield, a sponsor logo, a "launch on Binder" button, not
something that represents the 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 -- and pages whose only images are all externally
hosted -- keep whatever site-wide `ogp_image` you have configured. A page can
also opt out of selecting one of its own images on purpose, with
`.. autoopengraph_thumbnail:: none`, for a page whose images exist but are not
representative of it (say, a landing page with real content further down, but
whose own site-wide `ogp_image` -- a logo, a banner -- is what you actually
want shared):

```rst
.. autoopengraph_thumbnail:: none
```

**This includes the site's root page.** A common shape for one is a landing
page with its own inline images -- example plots, screenshots -- below an
introduction. Without `:: none` on that page specifically, its preview is one
of *those* images, not `ogp_image`, even though `ogp_image` is what most
projects set up expecting it to double as the whole site's default preview.
`ogp_image` is only ever a *fallback* for a page with no image of its own; it
is never assumed to be what the root page wants just because it is the root
page.

### 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
```

### Image metadata

When the selected image is one this build rendered (as opposed to, say, an
externally hosted `.. image::`, or an explicit `:og:image:` override),
`og:image:width`, `og:image:height` and `og:image:type` are filled in from the
file itself. Consumers that lay out a preview before fetching the image --
LinkedIn among them -- use these to avoid guessing its aspect ratio.

`og:image:alt` is taken from the selected image's own `alt` text, when it has
one, in place of the generic site name or page title `sphinxext-opengraph`
would otherwise fall back to.

## 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
```
