# AST Variation in MyST Universal AST
> [!Caution]
> This has migrated to the JB team notes: https://hackmd.io/@jupyterbook/rk7s9W9PZx
---
> [!Note] TLDR
> - Authors may wish to author content with ahead-of-time (AOT) variants, such as course material with instructor-specific asides.
> - This content may also be shared via publication methods (MyST sites).
> - At share time, we may not know which variants are required by the end user.
> - We can support this through new node type(s) and a universal AST.
This document provides a working discussion of how to handle content variation in universal MyST AST.
> [!Tip] Goals for Readers
> - To clarify unclear wording.
> - To annotate the document with questions.
## Context
> [!Note] Content Negotation & Content Variation
> In the pursuit of [Universal AST in the MyST Document Engine](/q0KUwIf_TLCgMyEORaK8kA), it is our intention that a MyST AST can represent the source-of-truth for a document. In practice, documents are often multi-purpose — users may wish to publish a single document to multiple formats that impose different requirements, and/or publish the same document to a variety of audiences. These two requirements can be descibed as **content negotiation** and **content variation**. Ultimately, the idea here is that the (published) AST needs to be a superset of state that is required by the final export configuration.
In this document, we're talking about **content variation**.
The Sphinx Document Engine supports an `only` directive. This directive accepts an _expression_ consistenting of boolean algebra and boolean tags that conditionally includes (or excludes) some content, e.g.
````rst
.. only:: html and draft
````
This can be used _both_ to restrict certain content to a particular export type, _and_ to specialise content to a particular audience. We might wish to add this to the MyST Engine.
## Examples
Here are some examples of situations that we need to consider.
> [!Warning] Ignore Outputs
> I'm explicitly ignoring output nodes in this document. I want to establish a case for AST variations separately to that, before figuring out whether the two overlap.
### Content Tagging (Personas)
An instructor might author material that is designed for mixed-competency classes:
````markdown
::::{conditional} needs_intro
% Explain what Parseval's theorem is.
:::{embed} #parsevals-theorem
:::
::::
Use Parsevals' theorem to show X.
````
Using this `conditional` directive (analogue of `only`), the instructor can build two PDFs, one with `needs_intro` and one without.
### Graceful Fallback (Manual)
In a web publication describing a new widget framework, an author includes an example widget as a figure. Widgets do not work in static export formats, and so the author wishes to handle this case explicitly through an explicit fallback.
Right now we have the concept of a `placeholder`. We do not have a distinction between
- A placeholder image for widgets that need kernels to render anything
- A placeholder image for Typst exports that can't render widgets at all.
We've previously talked about possibly needing to introduce a new `placeholder`-like thing to add this extra degree of freedom.
One idea would be to replace the `placeholder` feature with **content variation**:
``````markdown
% For site builds
````{choice} site
```{figure} #code-cell
This figure demonstrates my fancy new widget!
```
````
% For PDFs
````{choice} not site
```{figure} ./widget-nice-graphic.webp
This figure shows a screenshot of my fancy new widget! You can see it in production at <https://foo.com/widget>.
```
````
``````
As an aside, you might wonder how we'll handle label degeneracy (each `choice` adding different `label` IDs). This is a problem if left unchecked, because it means that xrefs become conditional "this might work if you're building for the right target".
There are a couple of solutions:
1. We add a new node `one-of`, and throw an error if a `label` appears under any `choice`. This forces users to set the label on the parent, which is always there.
``````markdown
`````{one-of}
:label: fig-widget
% For site builds
````{choice} site
```{figure} #code-cell
This figure demonstrates my fancy new widget!
```
````
% For PDFs
````{choice} not site
```{figure} ./widget-nice-graphic.webp
This figure shows a screenshot of my fancy new widget! You can see it in production at <https://foo.com/widget>.
```
````
`````
``````
Here, the `one-of` is erased at **export** time.
1. We could be more permissive and add a transform that lifts the `label` of any child of an `one-of` to the `one-of` itself, and throw errors for multiple labels.
> [!Note]
> This approach requires the `one-of` parent node in order to ensure we (a) have somewhere to place the `label`, and (b) can represent the concept of a finite set of choices.
>
> If we introduce a label lifting transformation, then we _need_ to ensure that the `choice` nodes are orthogonal. By treating `one-of` like a `switch` / `match` block, i.e. the first case wins, we can impose this constraint.
## Future Work
## Related Issues