Skip to content
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## Unreleased

### Added

- Options for customizing proof directive title format. [#163](https://github.com/executablebooks/sphinx-proof/pull/163)
- Possibility for shared numbering of groups of directives, fixing [\#64](https://github.com/executablebooks/sphinx-proof/issues/64). [\#https://github.com/executablebooks/sphinx-proof/pull/161](https://github.com/executablebooks/sphinx-proof/pull/161)

### Fixed

- Inconsistencies and missing colors within CSS styles. [b337c21](https://github.com/executablebooks/sphinx-proof/commit/b337c21675464224a4418beb9e70d3b6dc8e4127)
- Missing documentation for notation directive. [6f33744](https://github.com/executablebooks/sphinx-proof/commit/6f33744544b7596a30beba5b8807491d642b77a7) and [b15c7c0](https://github.com/executablebooks/sphinx-proof/commit/b15c7c09adefe85e0fdb8dea6dc460bd0f51fb1f)
- Nesting of unnumbered directives, fixing [\#165](https://github.com/executablebooks/sphinx-proof/issues/165).

## v0.3.0 (2025-10-20)

### NEW ✨
Expand Down
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@

This package contains a [Sphinx](http://www.sphinx-doc.org/) extension
for producing proof, theorem, axiom, lemma, definition, criterion, remark, conjecture,
corollary, algorithm, example, property, observation, proposition and assumption directives.
corollary, algorithm, example, property, observation, proposition, assumption and notation directives.

## Features

- **15 directive types** for mathematical proofs and theorems
- **Automatic numbering** of directives
- optional shared numbering of groups of directives
- **Cross-referencing** support via `prf:ref` role
- **33 languages supported** - Complete translations for all directive types in English plus 32 additional languages (Arabic, Bengali, Bulgarian, Chinese, Czech, Danish, Dutch, Finnish, French, German, Greek, Hebrew, Hindi, Hungarian, Indonesian, Italian, Japanese, Korean, Malay, Norwegian, Persian, Polish, Portuguese, Romanian, Russian, Spanish, Swedish, Thai, Turkish, Ukrainian, Urdu, Vietnamese)
- **Customizable styling** with multiple theme options

- **Customizable styling** with:
- two theme options
- directive title format customization

## Get started

Expand Down
2 changes: 2 additions & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,5 @@
# MyST Parser Configuration

myst_enable_extensions = ["dollarmath", "amsmath"]

prf_realtyp_to_countertyp = {}
2 changes: 1 addition & 1 deletion docs/source/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ for producing [proof](syntax:proof), [theorem](syntax:theorem), [axiom](syntax:a
[definition](syntax:definition), [criterion](syntax:criterion), [remark](syntax:remark),
[conjecture](syntax:conjecture),[corollary](syntax:corollary), [algorithm](syntax:algorithm),
[example](syntax:example), [property](syntax:property), [observation](syntax:observation),
[proposition](syntax:proposition) and [assumption](syntax:assumption) directives.
[proposition](syntax:proposition), [assumption](syntax:assumption) and [notation](syntax:notation) directives.

**Features**:

Expand Down
112 changes: 110 additions & 2 deletions docs/source/options.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Options

## Minimal color scheme

This package has the option to choose a more **minimal** color scheme.

The aim is to create admonitions that are clearly different to the core text with
Expand All @@ -15,7 +17,7 @@ compared to the current default

To enable the `minimal` color scheme you can use the following.

## Jupyter Book Project
### Jupyter Book Project

Add `proof_minimal_theme = True` to your `_config.yml`

Expand All @@ -25,6 +27,112 @@ sphinx:
proof_minimal_theme: true
```

## Sphinx Project
### Sphinx Project

Add `proof_minimal_theme = True` to your `conf.py`

## Shared numbering

By default, each type of (prf-)directive has their own numbering and counter. This can be changed by setting the option `prf_realtyp_to_countertyp` to a dictionary associating to a directive which the counter of which directive it should use.

### Sphinx Project

In `conf.py`, e.g. to have a shared counter for all directives:

```
prf_realtyp_to_countertyp = {
"axiom": "theorem",
"theorem": "theorem",
"lemma": "theorem",
"algorithm": "theorem",
"definition": "theorem",
"remark": "theorem",
"conjecture": "theorem",
"corollary": "theorem",
"criterion": "theorem",
"example": "theorem",
"property": "theorem",
"observation": "theorem",
"proposition": "theorem",
"assumption": "theorem",
"notation": "theorem",
}
```

In the following case, the directives `lemma`, `conjecture`, `corollary` and `proposition` will share the counter with `theorem`, while `axiom` and `assumption` will share the counter with `definition`. All other directives would use their original counter.


```
prf_realtyp_to_countertyp = {
"lemma": "theorem",
"conjecture": "theorem",
"corollary": "theorem",
"proposition": "theorem",
"axiom": "definition",
"assumption": "definition",
}
```

````{warning}
The association of a counter to a directive is not transitive: Let us consider the following configuration:

```
prf_realtyp_to_countertyp = {
"lemma": "theorem",
"conjecture": "lemma",
}
```

The `lemma` and `theorem` directives share a counter, however the `conjecture` directive has a separate counter (the `lemma` counter which is **not** used by `lemma` directives).
````

## Title format

By default, the directive titles are formatted as `Name x.y.z (Title)`, where `Name` is the name of the directive (e.g., Proof, Theorem, Definition), `x.y.z` is the numbering of the directive, and `Title` is the optional title provided by the user.

If no title is provided, only `Name x.y.z` is displayed.

The font weight of the entire title (`Name x.y.z (Title)` or `Name x.y.z`) is set to `--pst-admonition-font-weight-heading` by default, which commonly results in a semi-bold appearance.

In the reminder we call the part `Name x.y.z` the "number" and the part `(Title)` the "title".

You can customize the title format using the `proof_title_format` option:

- This option allows you to define how the title should be displayed by using `%t` as a placeholder for the user-provided title.
- The default format is ` (%t)`.
- A value of an empty string will result in no title being displayed.
- A `markdown` string can be used to format the title.
- For example, ` *%t*` will emphasize the title and contain no brackets.

Note that the initial part of the title (i.e., `Name x.y.z`) is not customizable and will always be displayed.

The font weight of the title can be adjusted using the `proof_title_weight` option:

- Any valid CSS font-weight value can be used, such as `normal`, `bold`, `bolder`, `lighter`, or numeric values like `400`, `700`, etc.
- Default value is `var(--pst-admonition-font-weight-heading)`.

The font weight of the number can be adjusted using the `proof_number_weight` option:
- Any valid CSS font-weight value can be used, such as `normal`, `bold`, `bolder`, `lighter`, or numeric values like `400`, `700`, etc.
- Default value is `var(--pst-admonition-font-weight-heading)`.

### Jupyter Book Project

Add `proof_title_format`, `proof_number_weight` and/or `proof_title_weight` to your `_config.yml`

```yaml
sphinx:
config:
proof_title_format: " *%t*"
proof_number_weight: "bold"
proof_title_weight: "normal"
```

### Sphinx Project

Add `proof_title_format`, `proof_number_weight` and/or `proof_title_weight` to your `conf.py`

```python
proof_title_format = " *%t*"
proof_number_weight = "bold"
proof_title_weight = "normal"
```
37 changes: 37 additions & 0 deletions docs/source/syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,43 @@ This is a dummy assumption directive.

You can refer to an assumption using the `{prf:ref}` role like: ```{prf:ref}`my-assumption` ```, which will replace the reference with the assumption number like so: {prf:ref}`my-assumption`. When an explicit text is provided, this caption will serve as the title of the reference.

(syntax:notation)=
### Notations

A notation directive can be included using the `prf:notation` pattern. The directive is enumerated by default and can take in an optional title argument. The following options are also supported:

* `label` : text

A unique identifier for your notation that you can use to reference it with `{prf:ref}`. Cannot contain spaces or special characters.
* `class` : text

Value of the notation’s class attribute which can be used to add custom CSS or JavaScript.
* `nonumber` : flag (empty)

Turns off notation auto numbering.

**Example**

```{prf:notation}
:label: my-notation

This is a dummy notation directive.
```

**MyST Syntax**

``````md
```{prf:notation}
:label: my-notation

This is a dummy notation directive.
```
``````

#### Referencing Notations

You can refer to a notation using the `{prf:ref}` role like: ```{prf:ref}`my-notation` ```, which will replace the reference with the notation number like so: {prf:ref}`my-notation`. When an explicit text is provided, this caption will serve as the title of the reference.

## How to Hide Content

Directive content can be hidden using the `dropdown` class which is available through [sphinx-togglebutton](https://sphinx-togglebutton.readthedocs.io/en/latest/). If your project utilizes the [MyST-NB](https://myst-nb.readthedocs.io/en/latest/) extension, there is no need to activate `sphinx-togglebutton` since it is already bundled with `MyST-NB`.
Expand Down
87 changes: 87 additions & 0 deletions sphinx_proof/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,39 @@ def copy_asset_files(app: Sphinx, exc: Union[bool, Exception]):
if exc is None:
for path in asset_files:
copy_asset(path, str(Path(app.outdir).joinpath("_static").absolute()))
# if needed, load css to memory,
# adjust font-weight according to user's setting in config
# and write to output static file
if app.config.proof_number_weight or app.config.proof_title_weight:
# only if at least one of the two options is set
path = str(Path(app.outdir).joinpath("_static", "proof.css").absolute())
with open(path, "r", encoding="utf-8") as f:
css_content = f.read()
if app.config.proof_number_weight:
css_content = css_content.replace(
"div.proof > p.admonition-title > span.caption-number {\n font-weight: var(--pst-admonition-font-weight-heading);\n}", # noqa: E501
f"div.proof > p.admonition-title > span.caption-number {{\n font-weight: {app.config.proof_number_weight};\n}}", # noqa: E501
)
if app.config.proof_title_weight:
css_content = css_content.replace(
"div.proof > p.admonition-title {\n font-weight: var(--pst-admonition-font-weight-heading);\n}", # noqa: E501
f"div.proof > p.admonition-title {{\n font-weight: {app.config.proof_title_weight};\n}}", # noqa: E501
)
out_path = Path(app.outdir).joinpath("_static", os.path.basename(path))
with open(out_path, "w", encoding="utf-8") as f:
f.write(css_content)


def setup(app: Sphinx) -> Dict[str, Any]:

app.add_config_value("proof_minimal_theme", False, "html")
app.add_config_value("prf_realtyp_to_countertyp", {}, "html")
app.add_config_value("proof_title_format", " (%t)", "html")
app.add_config_value("proof_number_weight", "", "html")
app.add_config_value("proof_title_weight", "", "html")

app.add_css_file("proof.css")
app.connect("config-inited", check_config_values)
app.connect("build-finished", copy_asset_files)
app.connect("config-inited", init_numfig)
app.connect("env-purge-doc", purge_proofs)
Expand Down Expand Up @@ -118,3 +144,64 @@ def setup(app: Sphinx) -> Dict[str, Any]:
"parallel_read_safe": True,
"parallel_write_safe": True,
}


def check_config_values(app: Sphinx, config: Config) -> None:
"""Check configuration values."""
# Check if proof_minimal_theme is boolean
if not isinstance(config.proof_minimal_theme, bool):
logger.warning(
"'proof_minimal_theme' config value must be a boolean. "
"Using default value False."
)
config.proof_minimal_theme = False

# Check of prf_realtyp_to_countertyp is a dictionary
if not isinstance(config.prf_realtyp_to_countertyp, dict):
logger.warning(
"'prf_realtyp_to_countertyp' config value must be a dictionary. "
"Using default empty dictionary."
)
config.prf_realtyp_to_countertyp = {}
# Check if each key and each value in prf_realtyp_to_countertyp
# is a valid proof type
for key, value in config.prf_realtyp_to_countertyp.items():
if key not in PROOF_TYPES:
logger.warning(
f"Key '{key}' in 'prf_realtyp_to_countertyp' is not "
"a valid proof type. "
"It will be removed."
)
del config.prf_realtyp_to_countertyp[key]
elif value not in PROOF_TYPES:
logger.warning(
f"Value '{value}' in 'prf_realtyp_to_countertyp' is not "
"a valid proof type. It will be removed."
)
del config.prf_realtyp_to_countertyp[key]
# Check if proof_title_format is a string
if not isinstance(config.proof_title_format, str):
logger.warning(
"'proof_title_format' config value must be a string."
"Using default value ' (%t)'."
)
config.proof_title_format = " (%t)"
elif "%t" not in config.proof_title_format:
logger.warning(
"'proof_title_format' config value must contain the "
"substring '%t' to print a title."
)
# Check if proof_number_weight is a string
if not isinstance(config.proof_number_weight, str):
logger.warning(
"'proof_number_weight' config value must be a string. "
"Using default value ''."
)
config.proof_number_weight = ""
# Check if proof_title_weight is a string
if not isinstance(config.proof_title_weight, str):
logger.warning(
"'proof_title_weight' config value must be a string. "
"Using default value ''."
)
config.proof_title_weight = ""
25 changes: 24 additions & 1 deletion sphinx_proof/_static/minimal/proof.css
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
--corollary-border-color: #c500c1;
--example-border-color: #f9377b;
--property-border-color: #fdf914;
--obseration-border-color: #7707ff;
--observation-border-color: #7707ff;
--proposition-border-color: #4f7aa8;
--assumption-border-color: #07fffb;
--notation-border-color: rgb(0, 255, 200);

/* --note-title-color: rgba(68,138,255,.1);
--note-border-color: #007bff;
Expand All @@ -38,6 +39,15 @@ div.proof > .admonition-title::before {
content: none;
}

/* Set font weights */
div.proof > p.admonition-title {
font-weight: var(--pst-admonition-font-weight-heading) !important;
}

div.proof > p.admonition-title > span.caption-number {
font-weight: var(--pst-admonition-font-weight-heading) !important;
}

/*********************************************
* Proof *
*********************************************/
Expand Down Expand Up @@ -209,6 +219,7 @@ div.proposition {
div.proposition > .admonition-title {
background-color: transparent;
}

/*********************************************
* Assumption *
*********************************************/
Expand All @@ -220,3 +231,15 @@ div.assumption {
div.assumption > .admonition-title {
background-color: transparent;
}

/*********************************************
* Notation *
*********************************************/
div.notation {
border-color: var(--notation-border-color);
background-color: none;
}

div.notation > .admonition-title {
background-color: transparent;
}
Loading