# 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