Differences from a Sphinx build

The ubCode preview is built for instant feedback while you type: it parses and renders your document on every keystroke, so it deliberately skips the slower, whole-project transforms that a full sphinx-build runs at the end. This page is the honest register of the resulting user-visible differences, so you can trust what you see and know when to reach for a real build.

Every entry below describes the Rendered preview mode (in Structural mode every directive and role renders as a uniform placeholder; see Authoring). Unless a status line says otherwise, none of these differences affect the correctness of background indexing or diagnostics — they change only what the preview panel displays.

The main entries follow the same shape — what you write, what the preview shows, what a Sphinx build does, and a short status note; smaller differences are grouped into bullet lists.

Not yet rendered

These constructs are recognised and shown as self-describing placeholders, but the transform that gives them their final form does not run in the preview yet.

Footnotes and citations

You write footnote and citation references such as [1]_, [#]_, or [CIT2002]_, with their matching definitions.

The preview shows each reference as a superscript label, but the label is not linked or resolved to its definition.

A Sphinx build numbers auto footnotes, resolves each reference, and renders bidirectional links between reference and definition.

Status — reference resolution is a whole-document transform the preview does not run. Indexing and diagnostics are unaffected.

Hyperlink references

You write a named reference such as target_ or `phrase`_ to a target defined in the same document — an external link target (.. _t: https://example.com), an indirect target (.. _alias: t_), an explicit internal target, or a section title.

The preview shows the reference linked to a slug guessed from its name (#target): correct for a same-page section or internal target, but a dead link for an external or indirect target.

A Sphinx build (and ubc build html) resolves the reference through the document’s target table — a reference to an external target renders the real URL, a reference to an indirect target follows the chain to its resolved end, and a reference to no target (or to a duplicated name) is reported as an error.

Statusubc build html resolves external and indirect references, and this does affect diagnostics: a dangling or ambiguous named reference now raises an error (ref.unknown_target, ref.duplicate_target, ref.circular_indirect, ref.nonexistent_indirect, ref.anonymous_mismatch) in indexing, ubc check, and the build warning count. The live preview keeps the slug guess (reference resolution is a whole-document transform it does not run per keystroke).

Math

You write a formula with the :math: role or a .. math:: block.

The preview shows the raw source, not typeset.

A Sphinx build typesets the math (via MathJax, KaTeX, or images).

Status — no math typesetting engine is bundled with the preview yet. Indexing and diagnostics are unaffected.

Needs flow diagram (needflow)

You write a .. needflow:: traceability graph.

A Sphinx build draws it from the resolved need set via the configured Graphviz or PlantUML engine (an SVG/PNG image).

The preview and ubc build html draw the same graph as a Mermaid flowchart, rendered client-side by the bundled Mermaid runtime (the list and table views render their real output too; the needpie and needbar charts remain placeholders — see below).

Status — implemented, with these deliberate differences from Sphinx-Needs (the output is self-consistent, not byte-identical to either upstream engine):

  • Engine — ubCode always renders Mermaid. :engine: (graphviz/plantuml) is accepted and ignored. :config: (Graphviz/PlantUML style snippets) is likewise accepted and ignored, except the two PlantUML direction names — lefttoright selects a left-to-right flowchart and toptobottom a top-down one (the default). :scale: and :alt: are accepted and ignored (they apply to a raster image, not an SVG).

  • Link types — with no :link_types: option the diagram draws edges of every configured link type (except parent_needs, which is never drawn), matching Sphinx-Needs’ effective behaviour: its needflow directive defaults :link_types: to all link fields, so the needs_flow_link_types config (default ["links"]) is inert for needflow in Sphinx-Needs 8.3.0 — ubCode intentionally has no equivalent key.

  • Node cap — a default cap of 100 drawn nodes (with a shown-vs-total notice) keeps a large graph readable and within Mermaid’s edge limits; :max_items: 0 draws them all. Sphinx-Needs has no such option.

  • Flat, need-level graph — a need’s parts are not drawn as their own nodes: they are not matched as separate items (the project-wide convention the :need_count: role also follows), so an edge to a part collapses to that part’s base need. The parent/child hierarchy is likewise not drawn as nesting (Sphinx-Needs clusters child needs inside their parent).

  • No click-through — nodes are not yet click-through links (the Mermaid runtime is initialised in its strict security mode, which strips link bindings); use the surrounding need cards / references to navigate.

  • Caption — a :caption: renders as a caption paragraph rather than a docutils figure, and its inline markup is not parsed (as for diagram captions above).

  • ``:root_depth:`` — the :root_id: walk bounds by shortest-path distance from the root (a breadth-first walk), rather than Sphinx-Needs’ order-dependent last-recorded distance; the two agree except at the exact depth boundary of a diamond-shaped graph.

  • ``:border_color:`` — applied as a literal colour (a bare 3- or 6-digit hex such as FF0000 is accepted, as Sphinx-Needs does; a per-need variant expression is not resolved yet).

Needs chart views (needpie / needbar)

You write a .. needpie:: or .. needbar:: chart.

The preview shows a self-describing placeholder naming the directive and its options.

A Sphinx build draws the chart (Matplotlib) from the resolved need set.

Status — the chart-view transforms are not implemented in the ubCode engine yet; this applies to ubc build html as well as the live preview. Indexing and diagnostics over the underlying needs are unaffected.

Document structure

Docinfo

You write a document that opens with a bibliographic field list (:Author:, :Version:, :Date:, and so on).

The preview shows a plain field list, the same as any field list appearing mid-document.

A Sphinx build turns the leading field list into a bibliographic docinfo table.

Status — the preview does not model docinfo. Indexing and diagnostics are unaffected.

rst_prolog / rst_epilog

You write content in rst_prolog or rst_epilog (in your parsing configuration).

The preview shows the prologue and epilogue spliced as their own blocks at the very start and end of each RST document, at the document root — not merged into the document’s section structure. A prologue that begins with a heading never swallows the rest of the document. State set there, such as a default role, still applies to the whole document. A malformed prolog is silently not shown.

A Sphinx build appends the epilogue text inside the document’s trailing section, and a prologue that opens a section can absorb the whole document.

Status — the splice is structural rather than a raw text concatenation. Indexing and diagnostics are unaffected by where the splice lands; the background index splices the prologue and epilogue too, so state and substitutions defined there apply to both the preview and the index.

Included files

.. include:: expands inline in the Rendered preview. A few things differ from how a Sphinx build splices the same file textually:

  • State set inside an included file — a .. default-role::, for example — is scoped to that file in the preview. A Sphinx build lets that state leak into the rest of the host document.

  • A section heading in an included file that uses the same underline level as the host nests one level deeper in the preview than in a Sphinx build.

  • Clicking inside spliced content does not jump to the included file; sync-scroll maps spliced content back to the .. include:: line in the host document.

  • Include options the preview does not support are flagged with a diagnostic and ignored; the file is still spliced in.

Markdown documents splice too. A MyST {include}, {literalinclude} or {csv-table} with :file: reads its file on every surface — the Rendered preview, the background index, ubc check and ubc build html — exactly as the reStructuredText forms do. Three things are specific to a Markdown host:

  • The included file is parsed as Markdown, whatever its extension. A fragment is parsed in the syntax of the document that includes it, which is what both Sphinx and myst-parser do: including a .rst file into a Markdown document yields paragraphs and plain text, not directives. (The reverse also holds and is unchanged: a .. include:: of a .md file is parsed as reStructuredText.)

  • An included file’s headings nest inside the include, not into the page. They keep their relative structure — a ## under a # stays nested — but they never close the host’s sections, and an included heading never becomes the page’s title. They do appear in the page’s “on this page” contents, and a target under an included heading keeps that heading as its title. A Sphinx build integrates them into the page at their literal levels, so an included # can open a section beside the page title; it also means an included file that starts at ## renders one heading level deeper here than it does there. :heading-offset: therefore has no effect and raises a warning, as do :relative-docs: and :relative-images:.

  • An included file’s front matter is ignored, as in a Sphinx build.

A file that includes itself, directly or through a chain, reports directive.include_circular and shows an error message in place of that one include; the rest of the page renders normally. This applies to both formats and to every surface — the preview, ubc check, the background index and ubc build html. A Sphinx build with myst-parser has no cycle detection at all and crashes instead.

.. literalinclude:: renders the named file as a code block — in the Rendered preview, in ubc build html, and validated by ubc check. Its selection options — :lines:, :start-after: / :start-at:, :end-before: / :end-at:, :dedent:, :prepend: / :append: and :tab-width: — apply exactly as in a Sphinx build, in the same order. Three options are not applied; each raises a warning and the file still renders with the remaining options applied:

  • :pyobject: — selecting a Python class or function by name.

  • :diff: — rendering a unified diff against a second file.

  • :encoding: — files are always read as UTF-8. utf-8 and utf-8-sig are honoured (they are what a Sphinx build uses by default); any other value warns and the file is still read as UTF-8.

A leading byte-order mark is stripped, as in a Sphinx build — unless you ask for :encoding: utf-8 explicitly, which keeps it. A :tab-width: above 1000 is clamped.

A missing file, a marker that is not found, a line spec that selects nothing, and a conflicting option pair each show a visible error message in place of the block — the equivalent of the error a Sphinx build renders into the page. A file that exists but is not valid UTF-8 is reported as an encoding problem, not as a missing file. Line-number options behave exactly as they do for code blocks (see below).

The background index now splices .. include:: too: a target, need, or glossary term defined in an included file is registered in the host document — a reference to it from the host resolves — while goto-definition and the hydrated title still point at the included file where it was written. A default-role (or similar state) set inside an included file therefore applies within that file’s spliced content on both the preview and the index.

Note

Both the preview and the background index splice rst_prolog / rst_epilog, so they agree on spliced content. Between indexing passes, the per-keystroke checks hold back undefined substitution diagnostics — a reference might be defined in your prologue — until the next background pass catches up.

Raw content

.. raw:: html passes its body through to the output verbatim, in a block position and inside a substitution definition (.. |br| raw:: html) — which is what makes an rst_prolog-defined |br| render as a real line break on every page that references it. As in every Sphinx build, raw HTML is enabled by default and is emitted unescaped, so treat a raw block as content you are responsible for. Two controls exist for when that assumption does not hold; both are described under Trust controls below.

  • The format argument is a list: .. raw:: html latex emits in an HTML build, and .. raw:: latex is silently dropped, exactly as docutils does.

  • :class: wraps the payload in a <div> (block) or <span> (substitution) carrying those classes; without the option there is no wrapper markup at all.

  • :file: reads the named file and records it as a dependency, so editing that file re-renders the page that embeds it.

  • :url: is not supported — it would need a network fetch at build time. It raises a warning and the directive renders nothing.

  • :encoding: is accepted but files are always read as UTF-8 (a non-UTF-8 value warns); a leading byte-order mark is stripped.

  • Where docutils would insert a red error block into the page — a missing :file:, a file and body content, neither of them — ubCode raises a warning and renders nothing, rather than injecting an error into the byte stream you are controlling.

Trust controls

Raw content assumes the author of a page is a party you trust to write HTML into your output. Two independent controls apply when that is not true.

Disabling the directive — set raw_enabled to false (see Parsing):

[parse]
raw_enabled = false

Every .. raw::, and its MyST {raw} twin, then passes nothing through: it raises a directive.raw_disabled warning and renders a placeholder showing its own source, escaped, instead — a framed block, or an inline chip where the directive defines a substitution (so a |br|-style definition stays valid inside the paragraphs that use it). The :file: form is not read at all, so no dependency on it is recorded. This is the docutils setting of the same name (which Sphinx never changes), and it applies wherever a document is processed — ubc build, ubc check, and the preview alike. It is meant for pipelines that build contributions you do not control, such as an auto-deployed preview of an external pull request.

Like the docutils setting, it gates the raw directive only. HTML written directly in a Markdown document (<div>…</div> in a paragraph, or an HTML block) is not a directive, so raw_enabled does not affect it.

Escaping in an untrusted workspace — the preview panel can render raw HTML escaped rather than injected, showing the markup as text. Unlike raw_enabled this is not project configuration (a repository you do not trust also supplies its own ubproject.toml): it is driven by the editor’s own trust state, per session, and it covers both syntaxes — the raw directive and Markdown’s native raw HTML.

Note

In VS Code this control is currently redundant: the ubCode extension does not declare support for untrusted workspaces, so VS Code disables the extension entirely in one — a stronger guarantee than escaping, with no preview at all. The escaping path exists in the language server, and the editor client already reports its trust state on every render, so the two stay in step if that posture is ever relaxed.

Conditional content

.. only:: evaluates its tag expression (and / or / not, parentheses, and the a if b else c form) against the build’s tags: html, format_html and builder_html, plus everything in the top-level build_tags key (see Configuring a project with ubproject.toml). A truthy expression includes the body transparently; a falsy one excludes it from the built output entirely, while the preview shows it as a collapsed, greyed block so you can see it exists. An expression that cannot be parsed raises only.invalid_expression and the body is kept — the same fail-open direction a Sphinx build takes (it evaluates only in a post-transform that keeps the children on any error), so a typo can never silently delete a page’s content.

Warning

Content inside an excluded only block is not indexed. A Sphinx build parses the body at read time and removes it only when writing, so a :ref: into a target defined inside a falsy only still resolves there. ubCode decides earlier and discards the body entirely, so targets, needs and glossary terms written inside an excluded block are not registered, and an inbound reference to one renders as an unresolved reference chip. No build warning is raised for an unresolved reference today, so this breaks silently — define anything you reference from elsewhere outside the only block.

sphinx-design components

The :octicon: role renders natively — in the Rendered preview, in ubc build html, and validated by ubc check. Write :octicon:`name`, :octicon:`name;height` or :octicon:`name;height;classes`, as in a sphinx-design build; it emits the same inline <svg class="sd-octicon sd-octicon-{name} …">, so a stylesheet written against those classes keeps working.

  • Only a subset of the GitHub octicon set is embedded. The full set is 328 icons, an unreasonable weight to carry for a decorative role. A name outside the embedded subset raises directive.octicon_unknown and the role keeps its self-describing chip, so your source stays visible.

  • A height that is not a number followed by px, em or rem raises directive.octicon_invalid_height, and a class name containing anything but letters, digits, _ and - raises directive.octicon_invalid_class — the rendered SVG is emitted verbatim, so an unsafe class name is refused rather than escaped. Both keep the chip.

The sphinx-design directives render natively too — grid / grid-item / grid-item-card, card (with its card-header / card-footer sub-directives), dropdown, div, and button-link / button-ref. They emit the same sd-* classes and element nesting sphinx-design does, so a stylesheet written against those classes keeps working.

ubc build html ships its own minimal design stylesheet (_static/ubc-design.css) covering the 12-column responsive grid, the card, dropdown and button chrome, and the spacing, sizing, display, border and text utility families. A site with no sphinx-design markup neither links nor writes it. The stylesheet is not loaded in the live preview yet, so a grid there stacks rather than laying out in columns; the markup and the semantics are the same either way.

What differs from a sphinx-design build:

  • A card’s header and footer come from sub-directives. Write .. card-header:: and .. card-footer:: inside a card or grid-item-card; they are hoisted into their slots wherever you put them. The legacy ^^^ header separator is not supported — use card-header. A card-header or card-footer outside a card raises a warning and renders its content in place.

  • The legacy +++ footer separator is honoured, but write exactly three. ubCode splits the parsed body where sphinx-design splits the raw lines, which cuts both ways. Better: a +++ inside a code block, a literal block or a nested card is not treated as a separator here, where sphinx-design’s line scan claims it. Worse: a run of four or more + is a transition character in reStructuredText, so the parser claims it first — a standalone ++++ still splits, but a ++++ written directly under a line of text is read as a section underline and that line is dropped. That shape raises directive.card_separator_invalid telling you to write +++ or use card-footer.

  • A grid-item or grid-item-card outside a grid raises a warning (as it does in a sphinx-design build) and still renders, as does a grid child that is neither.

  • A dropdown’s default icon is not read from conf.py. sphinx-design lets a project set per-directive option defaults through the sd_custom_directives configuration value, which ubCode has no equivalent for, so dropdowns relying on it render without their configured icon. A :icon: naming an icon outside the embedded subset raises directive.octicon_unknown and the dropdown renders icon-less.

  • A card or dropdown title is parsed as reStructuredText, on both host formats — so an :octicon: role in a grid-item-card title renders as an icon. A button-link with no content falls back to showing its URL verbatim; a button-ref with no content shows the resolved title of its target, as in a sphinx-design build.

  • A button’s content is inline-only. It becomes the link text, and a link may only contain inline markup, so a single wrapping paragraph is unwrapped. Block content (a list, an admonition, a nested directive) produces invalid nesting — sphinx-design has the same limitation, since it inline-parses the content lines. Keep button content to one line of inline markup.

  • A link’s text has no wrapping <span>. sphinx-design wraps a button’s and a card link’s text in one because docutils requires a reference to hold an element child; no stylesheet rule targets it, so ubCode emits the text directly.

  • A component container carries no trailing docutils class. docutils’ writer appends that token to every container it writes; no sphinx-design selector uses it, so it is not reproduced. For the same reason the sd-card-text class is not stamped onto a card’s or dropdown’s body paragraphs — the ubCode stylesheet reproduces its one effect with a .sd-card-body > p:last-child rule instead.

  • ``:img-background:`` is ignored (with a warning), and card-carousel, article-info, the badge roles and the tab directives are not implemented — they still show the generic placeholder.

Needs and variants

Need directives render as need cards. How a card looks compared with a Sphinx-Needs build:

  • A hydrated card lists the fields you authored plus any added by needextend — not the full set of config-default fields a Sphinx-Needs build can show in layouts that list every field.

  • Cards and their links refresh after each background indexing pass completes — a bounded refresh, not one per keystroke.

  • A falsy .. if:: block stays visible but collapsed and greyed out, labelled with its condition; a Sphinx-Needs build omits the block entirely.

  • A need referenced only inside a non-indexed include fragment (for example an extensionless or excluded file) keeps its placeholder chip, because the background index does not look inside such fragments.

  • A needlist with no :sort_by: lists its needs in id order; a Sphinx-Needs build lists them in document order. ubCode sorts by id so the output is reproducible build-to-build — the incremental index has no stable document-processing order to preserve. Giving :sort_by: a field name orders by that field in both.

  • :need_count: counts needs only; a Sphinx-Needs build counts needs and their parts in the filtered case. (The plain total counts needs in both.) In particular, a count filtered on the is_part context constant renders 0 (need parts are not modelled), where a Sphinx-Needs build counts the parts.

  • A needtable and a needlist cap their rendered rows/items at 100 by default and append an honest “Showing the first 100 of N needs” notice when the cap bites; a Sphinx-Needs build renders every matched need. The cap keeps an unfiltered view on a large project from dominating render (and live-preview) times. Set :max_items: 0 for no cap, or :max_items: N for a specific limit; the cap applies after the filter and the sort.

  • A needtable renders as a static table inside a scroll frame with a sticky header row, whatever its :style: (DATATABLES, the Sphinx-Needs default, renders the same static table). Interactive client-side sorting, filtering, and pagination are a planned enhancement; the scroll frame and sticky header are the interim.

  • A needtable with no :sort: sorts by id (case-insensitively), matching a Sphinx-Needs build’s default; where two needs would tie on the sort key, ubCode breaks the tie by id rather than by document order (the same reproducibility reason as needlist above). The :columns: and :sort: options accept the same names as Sphinx-Needs; an unknown name raises a warning with a “did you mean” suggestion and renders an empty column (:columns:) or falls back to id order (:sort:).

See also

RST preview for how need cards hydrate, Variants for the variant authoring constructs, and the if directive for variant conditions.

Diagrams

Mermaid and PlantUML diagram directives render, with a few option differences:

  • :width: and :height: size the diagram container in the preview (a bare number is treated as pixels). A Sphinx build drops these options for Mermaid diagrams.

  • :alt:, :scale:, :config:, :title:, and :zoom: are parsed but not applied.

  • Caption text is rendered as plain text; inline markup inside a caption is not parsed.

Tables

.. tabularcolumns:: is a LaTeX-only marker and correctly renders nothing, matching an HTML sphinx-build.

Code blocks

code-block (with its aliases sourcecode and code), :: literal blocks, literalinclude and .. highlight:: render with syntax highlighting, with a few differences from a Sphinx build:

  • The preview highlights client-side with a bundled set of common languages, not Pygments — colours differ from your Sphinx theme, and a language Pygments knows but the preview does not is shown as plain preformatted text (never mangled).

  • The bundled set includes rst/restructuredtext, myst, and cypher, highlighted with community grammars rather than Pygments lexers, so token boundaries can differ slightly from a Sphinx build.

  • :linenos:, :lineno-start:, and :emphasize-lines: are parsed but not applied — no line numbers or emphasised lines yet. The same holds for literalinclude, which also has :lineno-match:.

  • :dedent: and :force: are parsed but not applied on a code block. (literalinclude does apply :dedent: — it selects content there, rather than only re-indenting a block you already wrote.)

  • :caption: text is rendered as plain text above the block; inline markup inside a caption is not parsed (as for diagram captions above).

  • An argument-less code-block (or a plain :: block) with no .. highlight:: language and no configured highlight_language stays unhighlighted; a Sphinx build falls back to highlighting it as Python (its default).

Substitutions

|name| substitution references expand in the preview: your own .. |name| replace:: definitions, and the Sphinx specials |version|, |release|, and |today| (fed by the version, release, today, and today_fmt keys of the [project] table in ubproject.toml). A few differences from a Sphinx build remain:

  • An undefined or circular substitution keeps its literal |name| chip and raises a diagnostic; a Sphinx build reports these as errors in the build log instead.

  • Definitions whose expansion would grow without bound are cut off with an oversized diagnostic — a guard with no Sphinx equivalent (a Sphinx build can take unboundedly long on such input).

toctree navigation

.. toctree:: renders as a nested list of links to the listed documents, resolved from the toctree model of the last background indexing pass — like need cards, it refreshes after each pass completes, not on every keystroke. Display options (:maxdepth:, :titlesonly:, :includehidden:) shape the rendered list, and explicit Title <doc> entries keep their explicit titles. A Sphinx build additionally weaves every toctree into site-wide navigation (the sidebar and prev/next links of your theme); the preview shows only the in-page list.

Structure and inline detail

Some finer points of block and inline rendering differ from a Sphinx build:

  • Directive titles and arguments are not inline-parsed: markup in an admonition title, a version-marker line, or centered appears literally.

  • The body of parsed-literal shows inline markup literally.

  • Nested line blocks render flattened rather than progressively indented.

  • .. container:: ins and .. container:: del render as plain containers — insertion and deletion semantics are not applied.

  • Attribution lines (-- author) in epigraph, highlights, and pull-quote stay plain paragraphs rather than becoming attributions.

  • An unsupported option on a structural block makes the whole directive fall back to the generic placeholder.

Unknown directives and roles

Unknown directives

You write a directive ubCode does not recognise, such as .. wibble::.

The preview shows a generic directive placeholder.

A Sphinx build reports an error and typically drops or marks the construct.

Status — unknown directives also raise a configurable lint diagnostic. Register the directive with extend_directives, or silence it with ignore_directives, in your parsing configuration; see Linting.

Unknown roles

You write text marked up with an inline role ubCode does not recognise, such as a :wibble: role.

The preview shows a placeholder chip naming the role and its content.

A Sphinx build reports an error and typically drops or marks the construct.

Status — the chip is self-describing, so you can spot the unrecognised role at a glance.

ubCode-only view filters

The needlist view directive accepts a ubCode-only :cypher: option — a read-only Cypher selection (a bare WHERE-style expression such as n.status = 'open', or a full single-node-column query MATCH (n) ... RETURN n) — in place of the portable python filter surfaces.

You write a needlist with a :cypher: option.

The preview shows the needs the Cypher query selects, exactly as a :filter: selection would.

A Sphinx build reports an error: Sphinx-Needs has no :cypher: option, so the unknown option is a directive error.

Status:cypher: is a deliberate ubCode extension; Sphinx-Needs will not gain it, and the interoperability cost is accepted. It is mutually exclusive with the python filter surfaces (:filter: / :status: / :tags: / :types:); combining them raises a needs.filter_conflict warning and the Cypher selection wins.

Filter expression syntax

ubCode evaluates :filter: options and :need_count: targets with its own filter engine, which accepts the whole portable subset Sphinx-Needs documents (comparisons in either operand order, in / not in, is None, and / or / not, var.* variant fields, search(), and a bare True / False). A few forms Sphinx-Needs accepts are deliberately not accepted here; each raises a needs.filter_invalid warning (with the source column) and renders an empty result, and each has a workaround.

Raw-string prefix

You write a filter using a raw-string regex, such as search(r"[\w\s]+", title).

A Sphinx build accepts it (Python treats r"..." and "..." alike).

Status — drop the r prefix and write search("[\w\s]+", title). Regex escapes that are not also Python string escapes — \w, \d, \s — are preserved in an ordinary string, so the pattern is unchanged. But \b (word boundary), \n and \t do change meaning without the r prefix (Python reads them as backspace, newline, tab): keep those as an explicit character class or double the backslash (\\b). Note also that search() patterns must be valid for ubCode’s regex engine, which does not support lookaround ((?=...), (?<=...)) or backreferences (\1) — such patterns raise needs.filter_invalid.

Tuple or set containers

You write a membership test against a tuple or set literal, such as id in ("a", "b") or id in {"a", "b"}.

A Sphinx build accepts the tuple/set form.

Status — use a list literal instead: id in ["a", "b"].

Ordering comparisons against non-numbers

You write an ordering comparison (< / <= / > / >=) whose other operand is a string, boolean or None — in either operand order, such as deadline < "2024-06-01" or "beta" < status.

A Sphinx build accepts it: Python applies the operator to the values at runtime, and only raises if the types are not orderable.

Status — the ordering operators support numeric literals only (priority > 3, 5 <= level); equality (== / !=) and membership (in) work for strings. Use a numeric field for ordering. Date-string ordering has no direct portable equivalent — model the date as a sortable numeric field, or select with equality / in.

Unrecognised function calls

You write a filter calling a function other than the recognised helpers, such as bool(arch).

A Sphinx build accepts it (its eval has the Python builtins available).

Status — the only calls the engine recognises are search(), len(), .lower(), .upper(), .startswith() and .endswith(). For bool(arch), write the bare field arch — truthiness is supported (0, "" and [] are falsy, as in Python).

Images

  • A local image whose file lives outside your workspace folder does not load in the preview; the preview can only read files within the workspace.

  • Remote images referenced by an https:// URL load directly.

Closing the gap

The preview grows release by release: ubCode actively closes the gap with Sphinx and Sphinx-Needs — substitutions and toctree rendering moved off this page’s “not yet rendered” list recently — and the constructs still shown as placeholders, math and resolved footnotes, are tracked on the Roadmap. When you need the final, fully-transformed output, run a full sphinx-build.