Transformers
A transformer is a named, reusable piece of rendering logic loaded from a plugin's transformers/ folder and called from any string that supports placeholders with $apply-transformer(<id>).
Where a placeholder returns a value and a function computes one, a transformer produces presentation — one line, several lines, or none at all — out of a small declarative block: pick a branch, bucket a number, draw a progress bar, wrap a paragraph, repeat a template once per entry of a data provider.
Transformers replace the per-item lore-modifiers block, which is deprecated. A transformer is written once and named from every item, menu and dialog that needs it, instead of being copied into each item-stack.
Declaring a transformer
Every Astral plugin loads plugins/<Plugin>/transformers/ on startup and on reload — AstralCore and AstralItems both do. The folder is created if it does not exist, is walked recursively, and every .yml/.yaml file below it is read as one transformer.
Field | Type | Required | Description |
|---|---|---|---|
| String | Yes | The name |
| boolean | Yes |
|
| object | Yes | The transformer itself. Its |
| String | Yes | Type id. An unknown value fails the file with |
A file whose type is missing or unknown fails to load and is logged; the rest of the folder still loads. An id that is already registered — in the plugin's own registry or globally — is skipped with a Transformer with name '<id>' already exists, skipping... warning, so the first one loaded wins.
Scope and lookup
A call resolves against the registry of the plugin whose configuration the call was written in, which falls through to the global registry:
The owning plugin's own transformers (
global: false).The globally registered transformers (
global: true, from any plugin).
The owner is captured while the configuration file is deserialized, not when the line is rendered — so a lore line in an AstralItems blueprint keeps resolving against AstralItems' transformers no matter which plugin's code triggers the render. A string built outside a configuration read has no owner and therefore sees the global registry only.
Lookup is an exact, case-sensitive match. An unknown id fails the surrounding string with Transformer not found: <id>.
Calling a transformer
What a transformer returns decides what the line becomes:
Returned | Result on a lore line |
|---|---|
A single component | The line renders as that component. |
A list of components | The line is expanded into one line per element — see List-valued placeholders. |
An empty list | The line is removed entirely. |
Because a one-line result is a component and not a one-element list, the same transformer can also be named from a field that only accepts a single value — an item name, a menu title.
Transformer types
Type | Purpose |
|---|---|
Render one wrapped value or function call. | |
Alias of | |
Render the first group whose requirements pass. | |
Look the rendered value up in a map of cases. | |
Pick the first | |
Draw a row of slots — a page indicator, a pip row, a progress bar. | |
Break one run of text into as many lines as it needs. | |
Render one line per entry of a data provider. | |
As |
inline, switch, range, gauge and wrap are platform-independent; conditional, list and list-conditional are registered by the Paper module because they evaluate requirements and read Paper data providers.
inline
Renders a single wrapped value and returns whatever it resolves to. Useful to give a long expression or nested function call a name.
Field | Type | Required | Description |
|---|---|---|---|
| String (placeholder) | Yes | The value to resolve. Placeholders, |
inline-conditional is registered as a second name for the same type; the two are interchangeable.
conditional
Renders the first group whose requirements pass, or fallback when none do.
Field | Type | Required | Description |
|---|---|---|---|
|
| Yes | Named candidate branches. |
| int | No | Higher is tested first. Default |
| String \| | Yes | The branch content — one line or several. |
|
| No | Guards — see Requirements. Omitted, or an empty list, always passes. |
| String \| | No | Rendered when no group passes. Omitted means nothing is rendered and the line disappears. |
Groups are sorted by descending priority once, at load time; groups sharing a priority keep the order they were written in. A requirement that throws is logged to the console and its group is skipped, so evaluation continues with the next one.
switch
Looks the rendered value up in cases. This is the shape a conditional takes when every group compares the same placeholder to a literal, at the cost of one placeholder resolution and one hash lookup instead of a requirement walk per group.
Field | Type | Required | Description |
|---|---|---|---|
| String (placeholder) | Yes | The value to look up. |
|
| Yes | Case key → branch content. |
| String \| | No | Rendered on a miss. Omitted means the line disappears. |
Matching is textual and case-sensitive. An enum-valued placeholder renders to its constant name, so EXPIRED matches; booleans work with "true"/"false" keys (quote them — YAML would otherwise read them as booleans).
range
The numeric counterpart of switch: picks the first group whose [min, max] range contains value.
Field | Type | Required | Description |
|---|---|---|---|
| Number (placeholder) | Yes | The number to bucket. |
|
| Yes | Named buckets, tested in written order. |
| Number (placeholder) | Yes | Lower bound, inclusive. |
| Number (placeholder) | Yes | Upper bound, inclusive. |
| String \| | Yes | The branch content. |
| String \| | No | Rendered when no bucket matches. |
Groups are tested in the order they are written — where ranges overlap the earlier one wins, so order the narrow buckets first. A group whose min or max resolves to nothing is skipped. A value that resolves to nothing, or that no bucket covers, renders fallback; with no fallback the line disappears rather than rendering null.
gauge
Renders count slots on a single line, either marking one of them or filling up to one — a page indicator, a stat pip row, a progress bar. Returns one component.
Field | Type | Required | Description |
|---|---|---|---|
| int (placeholder) | Yes | Number of slots. A value |
| Number (placeholder) | Yes | In |
|
| No | Default |
| Number (placeholder) | No |
|
| String | Yes | The marked/filled slot glyph. |
| String | Yes | The unmarked/unfilled slot glyph. |
| String | No | Inserted between slots. Default: nothing. |
| String | No | Prepended to the line. |
| String | No | Appended to the line. |
markermarks the single slot atindex, counted from 1 so it reads like "page 2 of 3". An index outside1..countmarks nothing.fillfills every slot up toindex. Filling is floored, so a bar only reads as full once the value actually reaches the top; withoutmax,indexis the slot count itself, and withmaxthe ratio is scaled onto the slots and clamped at both ends. Amaxof0, or one that resolves to nothing, renders an empty gauge rather than dividing by it.
wrap
Breaks one run of text into as many lines as it needs, so a description is written as prose instead of being hand-wrapped and re-flowed after every edit. Returns a list of lines.
Field | Type | Required | Description |
|---|---|---|---|
| String | Yes | The text to wrap. MiniMessage and placeholders are resolved first. |
| int (placeholder) | Yes | Line width, in |
|
| No | Default |
| String | No | Prepended to every produced line and counted against |
Wrapping happens on the rendered component, so styling written mid-sentence survives the break: a colour opened before a break is still in effect on the next line. Consequences worth knowing:
Put the paragraph's base styling at the front of
content, not inprefix. The prefix is appended as a sibling and a sibling's style does not carry to the text after it.A word longer than the whole width overflows on a line of its own rather than being cut.
pixelsis approximate outside printable ASCII. Accented Latin, symbols and resource-pack glyphs (%img_*%) fall back to a typical glyph width.A
prefixat least as wide aswidthleaves one unit of room, so each word lands on its own line instead of looping.
list
Renders a single line template once per entry of a data provider, returning one line per entry. This is the replacement for the iterable lore modifier.
Field | Type | Required | Description |
|---|---|---|---|
| String (placeholder) | Yes | Resolves to the data source — same contract as a layout's provider, see Data Providers. A null result makes the call resolve to nothing. |
| String | Yes | The line template, rendered once per entry. |
| int (placeholder) | No | Caps how many entries are examined. Only applied when greater than |
|
| No | Per-entry filter. An entry whose requirements fail emits no line. |
Inside function (and inside requirement, which runs after the entry placeholders are injected):
Placeholder | Description |
|---|---|
| Zero-based index of the entry. |
| 1-based position ( |
| The entry itself, under the namespace its provider declares. |
The surrounding container is wrapped, so %player_…%, %variables_…%, %parameters_…% and every global namespace still resolve. Inherited parameter values are merged in after the entry keys and therefore win on a collision — inside a layout slot, %parameter_index% reports the layout's index and %parameter_position% is the reliable per-line counter.
list-conditional
As list, but each entry is rendered through a set of prioritised groups instead of a single template.
Field | Type | Required | Description |
|---|---|---|---|
| String (placeholder) | Yes | The data source — see Data Providers. |
|
| Yes | Named line templates, evaluated per entry. |
| int | No | Higher is evaluated first; ties keep written order. Default |
| String | Yes | The line emitted when this group passes. |
|
| No | Guards for this group, evaluated against the entry. Omitted means the group always emits. |
| int (placeholder) | No | Caps how many entries are examined. |
|
| No | Per-entry filter, evaluated before the groups. A failing entry emits nothing. |
Group and filter requirements see the same per-entry placeholders as list, and a requirement that throws skips that group (or, for filter, that entry).
Registering a custom type
A transformer type is a @ConfigSerializable class implementing Transformer, registered by class against the id configuration files select it with:
Member | Behaviour |
|---|---|
| Makes a type selectable as |
| Runs one render. Return a |
| Collapses a branch to what the caller expects: one line → a component, several → a list, missing or empty → |
| The shared registry. |
| The plugin's |
Call transformers().load() from your plugin's configuration-loading step — after the type registry is populated, since a file naming an unregistered type fails to load.
See also
Functions — the
$apply-transformer(...)call.Requirements — the requirement forms accepted by
conditional,listandlist-conditional.Menu Layouts — provider types accepted by
listandlist-conditional.Lore Modifiers — the deprecated per-item block transformers replace.