OpenFresco.Substitute (open_fresco v0.2.0)

Copy Markdown View Source

Inline token substitution — the templating model shared with phoenix_kit_og, ported for byte-compatibility so stored templates migrate without rewriting bindings.

Two bracket styles, both \w+ names, both resolved from a single values map:

  • {{slot}} — a template-local slot, wired to a concrete value per render (the post title, hero src, …). Listed by slots/1 for a wiring UI, with a :text / :image type inferred from where it appears.
  • [[global]] — a global that resolves from ambient context (site host, page URL, locale). Same substitution pass, different bracket; not listed as a slot.

Callers merge globals + wired slot values into one map (see OpenFresco.render_svg/3's :globals option).

The substitution contract (one path, both surfaces)

The editor stage and the PNG rasterizer resolve values through this exact module — there is no second substitution path — so what the author sees on the stage is what renders. The contract is:

  • Bracket styles{{slot}} (per-render wiring) and [[global]] (ambient context). Both name-match \w+.
  • Undefined tokens pass through literally. An unwired {{Title}} renders as the visible text {{Title}} — og's "needs wiring" signal — never as an error or empty string. This is deliberate: a missing wiring should be obvious in the card, not silent.
  • Escaping is the renderer's job, applied by default. Substitution returns plain text; OpenFresco.Svg XML-escapes every substituted value at the emission site. There is no "raw" opt-out — a template cannot inject markup through a slot value (see OpenFresco.Conformance). Hosts that need markup compose it into the scene as elements, not as substituted strings.
  • Types — non-binary values (nil, numbers, a whole-value %{placeholder: k}) are coerced (nil"", others via to_string/1); the placeholder form scans as its {{k}} token.

Deterministic: apply/2 is a pure function of (text, values).

Summary

Functions

Substitute {{slot}} and [[global]] tokens in text from values (string keys). Unknown names pass through unchanged. A nil text is "".

The {{slot}} names used across a scene, in first-appearance order, each tagged with the :text / :image type inferred from the field it appears in. Feeds a wiring UI (the phoenix_kit_og Assignments equivalent).

Whether a substituted string still carries an unresolved {{slot}} token (used by the image path to decide between drawing the resolved src and the neutral stand-in).

Functions

apply(text, values)

@spec apply(String.t() | %{placeholder: String.t()} | nil, map()) :: String.t()

Substitute {{slot}} and [[global]] tokens in text from values (string keys). Unknown names pass through unchanged. A nil text is "".

A whole-value placeholder (%{placeholder: "k"}) is accepted as a degenerate case — treated as the string "{{k}}".

slots(scene)

@spec slots(OpenFresco.Scene.t()) :: [%{name: String.t(), type: :text | :image}]

The {{slot}} names used across a scene, in first-appearance order, each tagged with the :text / :image type inferred from the field it appears in. Feeds a wiring UI (the phoenix_kit_og Assignments equivalent).

unresolved?(text)

@spec unresolved?(String.t()) :: boolean()

Whether a substituted string still carries an unresolved {{slot}} token (used by the image path to decide between drawing the resolved src and the neutral stand-in).