
A library mod that gives full control over animated, styled text overlays. Display rich on-screen messages with effects like rainbow gradients, neon glows, typewriter reveals, glitch distortion, and more… all composable and stackable.
Click any preview - every effect has an animated example in the docs.

Features
- Visual Effects: Rainbow, gradient, glitch, wave, shake, pulse, outline, and more… per-character and stackable
- Neon Glow: SDF-based halos around glyphs, with color, radius, softness, and pulse control
- Animations: Typewriter text, obfuscate reveal/hide, scrolling marquees
- Message Effects: Whole-message motion like slam, shatter, matrix, slide, breathe, and spin
- Markup Parser: XML-style tags for easy text styling
<rainbow>,<wave>;,<neon>,[slam] - MSDF Font Rendering: Crisp TrueType/OpenType fonts at any scale using Multi-Channel Signed Distance Fields with sharp corner reproduction
- Inline Rendering: Items and entities within text
- Server-Side Control: Send styled messages from server to clients
- Highly Customizable: Per-character effects, gradients, backgrounds, anchors, and queue channels
Commands
| Command | Description |
|---|---|
/eta send player duration text |
Send a styled message |
/eta queue player channel definition |
Queue sequential messages on a channel |
/eta test id |
Play a built-in demo message |
/eta test effect name |
Preview an individual effect |
/eta test combo effects |
Preview effect combinations |
For Developers
Embers Text API provides a full Java API for sending styled messages from the server. Register custom effects, parse markup, or build spans programmatically.
Check the documentation for integration guides and API reference.
For Pack Devs
- This is a library/api mod, it does nothing in gameplay on its own, but the command system lets you create and test effects in-game, and the inline markup (like
<rainbow>or<neon>) styles item names, tooltips, and quest descriptions! - Safe to include in any pack.
- Great for quest descriptions, tips, boss introductions, or any stylish text overlay.
- Works with KubeJS and datapacks! v3 includes built in KubeJS support!
All Versions
+ Add Version| Name | Game version | Platforms | Published | Downloads | Actions |
|---|---|---|---|---|---|
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 | ||||
| 20h ago | 1 |
## v3.0.4 (2026-09-16)
- Markup renders in text that mods draw as a plain string, such as Better Advancements
getString() and never reached the component parser.Font.drawInBatch(String), Font.prepareText(String) and Font.width(String) routestrings with real markup through the component path. Plain text with a stray
< or> is untouched. Font.split(String) and wordWrapHeight(String) still measure theraw string, so a wrapped plain-string label may wrap early.
<neon>halos now follow every drawn quad of a glyph: they rotate with<pendulum>
<swing>, clip to each <glitch> slice, use the fill colour and grow by theborder width with
<outline>, and match the random glyph drawn by <obfuscate>.- SDF fonts render with a cleaner edge. The text shader now measures the pixel
<pendulum>,<swing>) gets the same one pixel edge as straight text, the old edge sharpening thatthinned every stroke is gone, and small text gets a light stem darkening that fades out
above roughly GUI scale 4.
<bold> on an SDF font that has no _bold variant nowemboldens instead of drawing nothing extra, and a custom
px_range in a font manifestis honoured by the shader rather than assumed to be 8. Glyph bearings now use the real
margin of each baked glyph instead of a fixed pad, so wide and narrow letters no longer
sit up to half a unit apart from where the font places them. The on-disk SDF cache
format bumped to version 2 and rebuilds itself on first launch.
- Unknown or out-of-range effect params log one warning per effect and param per
- The KubeJS binding now ships on Forge 1.20.1, Fabric 1.20.1 and NeoForge 26.1 as well
EmbersText global and the EmbersTextEvents.itemTooltipclient event are the same on all four. The remaining targets have no KubeJS release to
bind to.
EmbersText.effects()returns a sorted list of every registered effect name, so a
EmbersText.clearItemNames()drops every rule registered withitemName, so a startup
## v3.0.4 (2026-09-16)
- Markup renders in text that mods draw as a plain string, such as Better Advancements
getString() and never reached the component parser.Font.drawInBatch(String), Font.prepareText(String) and Font.width(String) routestrings with real markup through the component path. Plain text with a stray
< or> is untouched. Font.split(String) and wordWrapHeight(String) still measure theraw string, so a wrapped plain-string label may wrap early.
<neon>halos now follow every drawn quad of a glyph: they rotate with<pendulum>
<swing>, clip to each <glitch> slice, use the fill colour and grow by theborder width with
<outline>, and match the random glyph drawn by <obfuscate>.- SDF fonts render with a cleaner edge. The text shader now measures the pixel
<pendulum>,<swing>) gets the same one pixel edge as straight text, the old edge sharpening thatthinned every stroke is gone, and small text gets a light stem darkening that fades out
above roughly GUI scale 4.
<bold> on an SDF font that has no _bold variant nowemboldens instead of drawing nothing extra, and a custom
px_range in a font manifestis honoured by the shader rather than assumed to be 8. Glyph bearings now use the real
margin of each baked glyph instead of a fixed pad, so wide and narrow letters no longer
sit up to half a unit apart from where the font places them. The on-disk SDF cache
format bumped to version 2 and rebuilds itself on first launch.
- Unknown or out-of-range effect params log one warning per effect and param per
- The KubeJS binding now ships on Forge 1.20.1, Fabric 1.20.1 and NeoForge 26.1 as well
EmbersText global and the EmbersTextEvents.itemTooltipclient event are the same on all four. The remaining targets have no KubeJS release to
bind to.
EmbersText.effects()returns a sorted list of every registered effect name, so a
EmbersText.clearItemNames()drops every rule registered withitemName, so a startup
## v3.0.4 (2026-09-16)
- Markup renders in text that mods draw as a plain string, such as Better Advancements
getString() and never reached the component parser.Font.drawInBatch(String), Font.prepareText(String) and Font.width(String) routestrings with real markup through the component path. Plain text with a stray
< or> is untouched. Font.split(String) and wordWrapHeight(String) still measure theraw string, so a wrapped plain-string label may wrap early.
<neon>halos now follow every drawn quad of a glyph: they rotate with<pendulum>
<swing>, clip to each <glitch> slice, use the fill colour and grow by theborder width with
<outline>, and match the random glyph drawn by <obfuscate>.- SDF fonts render with a cleaner edge. The text shader now measures the pixel
<pendulum>,<swing>) gets the same one pixel edge as straight text, the old edge sharpening thatthinned every stroke is gone, and small text gets a light stem darkening that fades out
above roughly GUI scale 4.
<bold> on an SDF font that has no _bold variant nowemboldens instead of drawing nothing extra, and a custom
px_range in a font manifestis honoured by the shader rather than assumed to be 8. Glyph bearings now use the real
margin of each baked glyph instead of a fixed pad, so wide and narrow letters no longer
sit up to half a unit apart from where the font places them. The on-disk SDF cache
format bumped to version 2 and rebuilds itself on first launch.
- Unknown or out-of-range effect params log one warning per effect and param per
- The KubeJS binding now ships on Forge 1.20.1, Fabric 1.20.1 and NeoForge 26.1 as well
EmbersText global and the EmbersTextEvents.itemTooltipclient event are the same on all four. The remaining targets have no KubeJS release to
bind to.
EmbersText.effects()returns a sorted list of every registered effect name, so a
EmbersText.clearItemNames()drops every rule registered withitemName, so a startup
## v3.0.4 (2026-09-16)
- Markup renders in text that mods draw as a plain string, such as Better Advancements
getString() and never reached the component parser.Font.drawInBatch(String), Font.prepareText(String) and Font.width(String) routestrings with real markup through the component path. Plain text with a stray
< or> is untouched. Font.split(String) and wordWrapHeight(String) still measure theraw string, so a wrapped plain-string label may wrap early.
<neon>halos now follow every drawn quad of a glyph: they rotate with<pendulum>
<swing>, clip to each <glitch> slice, use the fill colour and grow by theborder width with
<outline>, and match the random glyph drawn by <obfuscate>.- SDF fonts render with a cleaner edge. The text shader now measures the pixel
<pendulum>,<swing>) gets the same one pixel edge as straight text, the old edge sharpening thatthinned every stroke is gone, and small text gets a light stem darkening that fades out
above roughly GUI scale 4.
<bold> on an SDF font that has no _bold variant nowemboldens instead of drawing nothing extra, and a custom
px_range in a font manifestis honoured by the shader rather than assumed to be 8. Glyph bearings now use the real
margin of each baked glyph instead of a fixed pad, so wide and narrow letters no longer
sit up to half a unit apart from where the font places them. The on-disk SDF cache
format bumped to version 2 and rebuilds itself on first launch.
- Unknown or out-of-range effect params log one warning per effect and param per
- The KubeJS binding now ships on Forge 1.20.1, Fabric 1.20.1 and NeoForge 26.1 as well
EmbersText global and the EmbersTextEvents.itemTooltipclient event are the same on all four. The remaining targets have no KubeJS release to
bind to.
EmbersText.effects()returns a sorted list of every registered effect name, so a
EmbersText.clearItemNames()drops every rule registered withitemName, so a startup
## v3.0.4 (2026-09-16)
- Markup renders in text that mods draw as a plain string, such as Better Advancements
getString() and never reached the component parser.Font.drawInBatch(String), Font.prepareText(String) and Font.width(String) routestrings with real markup through the component path. Plain text with a stray
< or> is untouched. Font.split(String) and wordWrapHeight(String) still measure theraw string, so a wrapped plain-string label may wrap early.
<neon>halos now follow every drawn quad of a glyph: they rotate with<pendulum>
<swing>, clip to each <glitch> slice, use the fill colour and grow by theborder width with
<outline>, and match the random glyph drawn by <obfuscate>.- SDF fonts render with a cleaner edge. The text shader now measures the pixel
<pendulum>,<swing>) gets the same one pixel edge as straight text, the old edge sharpening thatthinned every stroke is gone, and small text gets a light stem darkening that fades out
above roughly GUI scale 4.
<bold> on an SDF font that has no _bold variant nowemboldens instead of drawing nothing extra, and a custom
px_range in a font manifestis honoured by the shader rather than assumed to be 8. Glyph bearings now use the real
margin of each baked glyph instead of a fixed pad, so wide and narrow letters no longer
sit up to half a unit apart from where the font places them. The on-disk SDF cache
format bumped to version 2 and rebuilds itself on first launch.
- Unknown or out-of-range effect params log one warning per effect and param per
- The KubeJS binding now ships on Forge 1.20.1, Fabric 1.20.1 and NeoForge 26.1 as well
EmbersText global and the EmbersTextEvents.itemTooltipclient event are the same on all four. The remaining targets have no KubeJS release to
bind to.
EmbersText.effects()returns a sorted list of every registered effect name, so a
EmbersText.clearItemNames()drops every rule registered withitemName, so a startup
## v3.0.4 (2026-09-16)
- Markup renders in text that mods draw as a plain string, such as Better Advancements
getString() and never reached the component parser.Font.drawInBatch(String), Font.prepareText(String) and Font.width(String) routestrings with real markup through the component path. Plain text with a stray
< or> is untouched. Font.split(String) and wordWrapHeight(String) still measure theraw string, so a wrapped plain-string label may wrap early.
<neon>halos now follow every drawn quad of a glyph: they rotate with<pendulum>
<swing>, clip to each <glitch> slice, use the fill colour and grow by theborder width with
<outline>, and match the random glyph drawn by <obfuscate>.- SDF fonts render with a cleaner edge. The text shader now measures the pixel
<pendulum>,<swing>) gets the same one pixel edge as straight text, the old edge sharpening thatthinned every stroke is gone, and small text gets a light stem darkening that fades out
above roughly GUI scale 4.
<bold> on an SDF font that has no _bold variant nowemboldens instead of drawing nothing extra, and a custom
px_range in a font manifestis honoured by the shader rather than assumed to be 8. Glyph bearings now use the real
margin of each baked glyph instead of a fixed pad, so wide and narrow letters no longer
sit up to half a unit apart from where the font places them. The on-disk SDF cache
format bumped to version 2 and rebuilds itself on first launch.
- Unknown or out-of-range effect params log one warning per effect and param per
- The KubeJS binding now ships on Forge 1.20.1, Fabric 1.20.1 and NeoForge 26.1 as well
EmbersText global and the EmbersTextEvents.itemTooltipclient event are the same on all four. The remaining targets have no KubeJS release to
bind to.
EmbersText.effects()returns a sorted list of every registered effect name, so a
EmbersText.clearItemNames()drops every rule registered withitemName, so a startup
## v3.0.4 (2026-09-16)
- Markup renders in text that mods draw as a plain string, such as Better Advancements
getString() and never reached the component parser.Font.drawInBatch(String), Font.prepareText(String) and Font.width(String) routestrings with real markup through the component path. Plain text with a stray
< or> is untouched. Font.split(String) and wordWrapHeight(String) still measure theraw string, so a wrapped plain-string label may wrap early.
<neon>halos now follow every drawn quad of a glyph: they rotate with<pendulum>
<swing>, clip to each <glitch> slice, use the fill colour and grow by theborder width with
<outline>, and match the random glyph drawn by <obfuscate>.- SDF fonts render with a cleaner edge. The text shader now measures the pixel
<pendulum>,<swing>) gets the same one pixel edge as straight text, the old edge sharpening thatthinned every stroke is gone, and small text gets a light stem darkening that fades out
above roughly GUI scale 4.
<bold> on an SDF font that has no _bold variant nowemboldens instead of drawing nothing extra, and a custom
px_range in a font manifestis honoured by the shader rather than assumed to be 8. Glyph bearings now use the real
margin of each baked glyph instead of a fixed pad, so wide and narrow letters no longer
sit up to half a unit apart from where the font places them. The on-disk SDF cache
format bumped to version 2 and rebuilds itself on first launch.
- Unknown or out-of-range effect params log one warning per effect and param per
- The KubeJS binding now ships on Forge 1.20.1, Fabric 1.20.1 and NeoForge 26.1 as well
EmbersText global and the EmbersTextEvents.itemTooltipclient event are the same on all four. The remaining targets have no KubeJS release to
bind to.
EmbersText.effects()returns a sorted list of every registered effect name, so a
EmbersText.clearItemNames()drops every rule registered withitemName, so a startup
## v3.0.4 (2026-09-16)
- Markup renders in text that mods draw as a plain string, such as Better Advancements
getString() and never reached the component parser.Font.drawInBatch(String), Font.prepareText(String) and Font.width(String) routestrings with real markup through the component path. Plain text with a stray
< or> is untouched. Font.split(String) and wordWrapHeight(String) still measure theraw string, so a wrapped plain-string label may wrap early.
<neon>halos now follow every drawn quad of a glyph: they rotate with<pendulum>
<swing>, clip to each <glitch> slice, use the fill colour and grow by theborder width with
<outline>, and match the random glyph drawn by <obfuscate>.- SDF fonts render with a cleaner edge. The text shader now measures the pixel
<pendulum>,<swing>) gets the same one pixel edge as straight text, the old edge sharpening thatthinned every stroke is gone, and small text gets a light stem darkening that fades out
above roughly GUI scale 4.
<bold> on an SDF font that has no _bold variant nowemboldens instead of drawing nothing extra, and a custom
px_range in a font manifestis honoured by the shader rather than assumed to be 8. Glyph bearings now use the real
margin of each baked glyph instead of a fixed pad, so wide and narrow letters no longer
sit up to half a unit apart from where the font places them. The on-disk SDF cache
format bumped to version 2 and rebuilds itself on first launch.
- Unknown or out-of-range effect params log one warning per effect and param per
- The KubeJS binding now ships on Forge 1.20.1, Fabric 1.20.1 and NeoForge 26.1 as well
EmbersText global and the EmbersTextEvents.itemTooltipclient event are the same on all four. The remaining targets have no KubeJS release to
bind to.
EmbersText.effects()returns a sorted list of every registered effect name, so a
EmbersText.clearItemNames()drops every rule registered withitemName, so a startup
## v3.0.3 (2026-08-10)
- A
<scroll>span is now a single unbreakable unit of layout. Multi-line scroll
from the per-frame
x progression, which restarts on every wrapped line, so itsized the whole marquee from the first line alone. A scroll span no longer wraps
and reserves only its viewport width. Text written after
</scroll> thereforestarts one viewport past the marquee rather than past the entire hidden string.
- New
[clamp]attribute andclampToScreen(boolean)builder method. Screen
partly off screen. Clamping also no longer inverts on a message larger than the
window, which used to snap it to the wrong corner.
- The anvil rename limit now counts visible characters. Vanilla's 50 still applies,
<rainbow>Excalibur</rainbow> is9 characters rather than 28. A 256-character raw ceiling keeps a name from being
padded with unlimited markup. On 1.20.1 and 1.21.1 the text field rejects an
over-long name as you type; on 26.1 and 26.2 the server declines the rename,
because those versions removed the text-field filter hook.
- Removed** the
anvil.nameMaxLengthserver config option. It is replaced by the
mixin targeted
setItemName while the length constant lives in validateName.- The continuous message effects
breathe,drift,float,grow,rock,
spin and vibrate take three new timing params, so they can run in burstsinstead of looping forever:
every (seconds between bursts, 0 for continuous),hold (seconds a burst lasts) and delay (seconds before the first burst).Each burst restarts the effect's own clock at zero, so a sine-driven effect
enters and leaves at rest rather than snapping mid-swing. Existing messages are
unaffected;
every defaults to 0.<item>and<entity>now take hover and click from an enclosing<hover>or
<click> tag. The parser rebuilt icon spans from scratch and dropped everythingbut the icon's own attributes, so wrapping an icon in
<click> did nothing.- New
tooltipattribute on<item>and<entity>.tooltip=trueshows the
count andnbt; any other value is used as literal tooltip text.<hover>acceptsaction=show_itemandaction=show_entity, so ordinary text
- The hover region of an inline icon now matches the sprite. It was the icon's
tuck into surrounding text, leaving the right edge dead. Chat is now hit-tested
from the drawn sprite rather than the advance.
- Hover tooltips now work in mod GUIs, not just chat. Vanilla only hit-tests text
or any other custom UI had nothing to hook. Each interactive glyph's on-screen
rect is now recorded as it is drawn and tested against the cursor in any screen,
clipped to the active scissor so scrolled-out text is not hoverable. 1.20.1 and
1.21.1 only; 26.1 and 26.2 use a different text pipeline and are not covered yet.
- Fixed inline icons vanishing once they carried a hover or click event. Deriving
withHoverEvent/withClickEvent dropped the icon's registry id,so the tooltip appeared over empty space. This also restores icons combined with
<type> and <obfuscate>, which lost them the same way.- Fixed the hover and click region of an inline icon. Icons draw 16px wide but rode
measured them the same way.
## v3.0.3 (2026-08-10)
- A
<scroll>span is now a single unbreakable unit of layout. Multi-line scroll
from the per-frame
x progression, which restarts on every wrapped line, so itsized the whole marquee from the first line alone. A scroll span no longer wraps
and reserves only its viewport width. Text written after
</scroll> thereforestarts one viewport past the marquee rather than past the entire hidden string.
- New
[clamp]attribute andclampToScreen(boolean)builder method. Screen
partly off screen. Clamping also no longer inverts on a message larger than the
window, which used to snap it to the wrong corner.
- The anvil rename limit now counts visible characters. Vanilla's 50 still applies,
<rainbow>Excalibur</rainbow> is9 characters rather than 28. A 256-character raw ceiling keeps a name from being
padded with unlimited markup. On 1.20.1 and 1.21.1 the text field rejects an
over-long name as you type; on 26.1 and 26.2 the server declines the rename,
because those versions removed the text-field filter hook.
- Removed** the
anvil.nameMaxLengthserver config option. It is replaced by the
mixin targeted
setItemName while the length constant lives in validateName.- The continuous message effects
breathe,drift,float,grow,rock,
spin and vibrate take three new timing params, so they can run in burstsinstead of looping forever:
every (seconds between bursts, 0 for continuous),hold (seconds a burst lasts) and delay (seconds before the first burst).Each burst restarts the effect's own clock at zero, so a sine-driven effect
enters and leaves at rest rather than snapping mid-swing. Existing messages are
unaffected;
every defaults to 0.<item>and<entity>now take hover and click from an enclosing<hover>or
<click> tag. The parser rebuilt icon spans from scratch and dropped everythingbut the icon's own attributes, so wrapping an icon in
<click> did nothing.- New
tooltipattribute on<item>and<entity>.tooltip=trueshows the
count andnbt; any other value is used as literal tooltip text.<hover>acceptsaction=show_itemandaction=show_entity, so ordinary text
- The hover region of an inline icon now matches the sprite. It was the icon's
tuck into surrounding text, leaving the right edge dead. Chat is now hit-tested
from the drawn sprite rather than the advance.
- Hover tooltips now work in mod GUIs, not just chat. Vanilla only hit-tests text
or any other custom UI had nothing to hook. Each interactive glyph's on-screen
rect is now recorded as it is drawn and tested against the cursor in any screen,
clipped to the active scissor so scrolled-out text is not hoverable. 1.20.1 and
1.21.1 only; 26.1 and 26.2 use a different text pipeline and are not covered yet.
- Fixed inline icons vanishing once they carried a hover or click event. Deriving
withHoverEvent/withClickEvent dropped the icon's registry id,so the tooltip appeared over empty space. This also restores icons combined with
<type> and <obfuscate>, which lost them the same way.- Fixed the hover and click region of an inline icon. Icons draw 16px wide but rode
measured them the same way.
## v3.0.3 (2026-08-10)
- A
<scroll>span is now a single unbreakable unit of layout. Multi-line scroll
from the per-frame
x progression, which restarts on every wrapped line, so itsized the whole marquee from the first line alone. A scroll span no longer wraps
and reserves only its viewport width. Text written after
</scroll> thereforestarts one viewport past the marquee rather than past the entire hidden string.
- New
[clamp]attribute andclampToScreen(boolean)builder method. Screen
partly off screen. Clamping also no longer inverts on a message larger than the
window, which used to snap it to the wrong corner.
- The anvil rename limit now counts visible characters. Vanilla's 50 still applies,
<rainbow>Excalibur</rainbow> is9 characters rather than 28. A 256-character raw ceiling keeps a name from being
padded with unlimited markup. On 1.20.1 and 1.21.1 the text field rejects an
over-long name as you type; on 26.1 and 26.2 the server declines the rename,
because those versions removed the text-field filter hook.
- Removed** the
anvil.nameMaxLengthserver config option. It is replaced by the
mixin targeted
setItemName while the length constant lives in validateName.- The continuous message effects
breathe,drift,float,grow,rock,
spin and vibrate take three new timing params, so they can run in burstsinstead of looping forever:
every (seconds between bursts, 0 for continuous),hold (seconds a burst lasts) and delay (seconds before the first burst).Each burst restarts the effect's own clock at zero, so a sine-driven effect
enters and leaves at rest rather than snapping mid-swing. Existing messages are
unaffected;
every defaults to 0.<item>and<entity>now take hover and click from an enclosing<hover>or
<click> tag. The parser rebuilt icon spans from scratch and dropped everythingbut the icon's own attributes, so wrapping an icon in
<click> did nothing.- New
tooltipattribute on<item>and<entity>.tooltip=trueshows the
count andnbt; any other value is used as literal tooltip text.<hover>acceptsaction=show_itemandaction=show_entity, so ordinary text
- The hover region of an inline icon now matches the sprite. It was the icon's
tuck into surrounding text, leaving the right edge dead. Chat is now hit-tested
from the drawn sprite rather than the advance.
- Hover tooltips now work in mod GUIs, not just chat. Vanilla only hit-tests text
or any other custom UI had nothing to hook. Each interactive glyph's on-screen
rect is now recorded as it is drawn and tested against the cursor in any screen,
clipped to the active scissor so scrolled-out text is not hoverable. 1.20.1 and
1.21.1 only; 26.1 and 26.2 use a different text pipeline and are not covered yet.
- Fixed inline icons vanishing once they carried a hover or click event. Deriving
withHoverEvent/withClickEvent dropped the icon's registry id,so the tooltip appeared over empty space. This also restores icons combined with
<type> and <obfuscate>, which lost them the same way.- Fixed the hover and click region of an inline icon. Icons draw 16px wide but rode
measured them the same way.
## v3.0.3 (2026-08-10)
- A
<scroll>span is now a single unbreakable unit of layout. Multi-line scroll
from the per-frame
x progression, which restarts on every wrapped line, so itsized the whole marquee from the first line alone. A scroll span no longer wraps
and reserves only its viewport width. Text written after
</scroll> thereforestarts one viewport past the marquee rather than past the entire hidden string.
- New
[clamp]attribute andclampToScreen(boolean)builder method. Screen
partly off screen. Clamping also no longer inverts on a message larger than the
window, which used to snap it to the wrong corner.
- The anvil rename limit now counts visible characters. Vanilla's 50 still applies,
<rainbow>Excalibur</rainbow> is9 characters rather than 28. A 256-character raw ceiling keeps a name from being
padded with unlimited markup. On 1.20.1 and 1.21.1 the text field rejects an
over-long name as you type; on 26.1 and 26.2 the server declines the rename,
because those versions removed the text-field filter hook.
- Removed** the
anvil.nameMaxLengthserver config option. It is replaced by the
mixin targeted
setItemName while the length constant lives in validateName.- The continuous message effects
breathe,drift,float,grow,rock,
spin and vibrate take three new timing params, so they can run in burstsinstead of looping forever:
every (seconds between bursts, 0 for continuous),hold (seconds a burst lasts) and delay (seconds before the first burst).Each burst restarts the effect's own clock at zero, so a sine-driven effect
enters and leaves at rest rather than snapping mid-swing. Existing messages are
unaffected;
every defaults to 0.<item>and<entity>now take hover and click from an enclosing<hover>or
<click> tag. The parser rebuilt icon spans from scratch and dropped everythingbut the icon's own attributes, so wrapping an icon in
<click> did nothing.- New
tooltipattribute on<item>and<entity>.tooltip=trueshows the
count andnbt; any other value is used as literal tooltip text.<hover>acceptsaction=show_itemandaction=show_entity, so ordinary text
- The hover region of an inline icon now matches the sprite. It was the icon's
tuck into surrounding text, leaving the right edge dead. Chat is now hit-tested
from the drawn sprite rather than the advance.
- Hover tooltips now work in mod GUIs, not just chat. Vanilla only hit-tests text
or any other custom UI had nothing to hook. Each interactive glyph's on-screen
rect is now recorded as it is drawn and tested against the cursor in any screen,
clipped to the active scissor so scrolled-out text is not hoverable. 1.20.1 and
1.21.1 only; 26.1 and 26.2 use a different text pipeline and are not covered yet.
- Fixed inline icons vanishing once they carried a hover or click event. Deriving
withHoverEvent/withClickEvent dropped the icon's registry id,so the tooltip appeared over empty space. This also restores icons combined with
<type> and <obfuscate>, which lost them the same way.- Fixed the hover and click region of an inline icon. Icons draw 16px wide but rode
measured them the same way.
## v3.0.3 (2026-08-10)
- A
<scroll>span is now a single unbreakable unit of layout. Multi-line scroll
from the per-frame
x progression, which restarts on every wrapped line, so itsized the whole marquee from the first line alone. A scroll span no longer wraps
and reserves only its viewport width. Text written after
</scroll> thereforestarts one viewport past the marquee rather than past the entire hidden string.
- New
[clamp]attribute andclampToScreen(boolean)builder method. Screen
partly off screen. Clamping also no longer inverts on a message larger than the
window, which used to snap it to the wrong corner.
- The anvil rename limit now counts visible characters. Vanilla's 50 still applies,
<rainbow>Excalibur</rainbow> is9 characters rather than 28. A 256-character raw ceiling keeps a name from being
padded with unlimited markup. On 1.20.1 and 1.21.1 the text field rejects an
over-long name as you type; on 26.1 and 26.2 the server declines the rename,
because those versions removed the text-field filter hook.
- Removed** the
anvil.nameMaxLengthserver config option. It is replaced by the
mixin targeted
setItemName while the length constant lives in validateName.- The continuous message effects
breathe,drift,float,grow,rock,
spin and vibrate take three new timing params, so they can run in burstsinstead of looping forever:
every (seconds between bursts, 0 for continuous),hold (seconds a burst lasts) and delay (seconds before the first burst).Each burst restarts the effect's own clock at zero, so a sine-driven effect
enters and leaves at rest rather than snapping mid-swing. Existing messages are
unaffected;
every defaults to 0.<item>and<entity>now take hover and click from an enclosing<hover>or
<click> tag. The parser rebuilt icon spans from scratch and dropped everythingbut the icon's own attributes, so wrapping an icon in
<click> did nothing.- New
tooltipattribute on<item>and<entity>.tooltip=trueshows the
count andnbt; any other value is used as literal tooltip text.<hover>acceptsaction=show_itemandaction=show_entity, so ordinary text
- The hover region of an inline icon now matches the sprite. It was the icon's
tuck into surrounding text, leaving the right edge dead. Chat is now hit-tested
from the drawn sprite rather than the advance.
- Hover tooltips now work in mod GUIs, not just chat. Vanilla only hit-tests text
or any other custom UI had nothing to hook. Each interactive glyph's on-screen
rect is now recorded as it is drawn and tested against the cursor in any screen,
clipped to the active scissor so scrolled-out text is not hoverable. 1.20.1 and
1.21.1 only; 26.1 and 26.2 use a different text pipeline and are not covered yet.
- Fixed inline icons vanishing once they carried a hover or click event. Deriving
withHoverEvent/withClickEvent dropped the icon's registry id,so the tooltip appeared over empty space. This also restores icons combined with
<type> and <obfuscate>, which lost them the same way.- Fixed the hover and click region of an inline icon. Icons draw 16px wide but rode
measured them the same way.
## v3.0.3 (2026-08-10)
- A
<scroll>span is now a single unbreakable unit of layout. Multi-line scroll
from the per-frame
x progression, which restarts on every wrapped line, so itsized the whole marquee from the first line alone. A scroll span no longer wraps
and reserves only its viewport width. Text written after
</scroll> thereforestarts one viewport past the marquee rather than past the entire hidden string.
- New
[clamp]attribute andclampToScreen(boolean)builder method. Screen
partly off screen. Clamping also no longer inverts on a message larger than the
window, which used to snap it to the wrong corner.
- The anvil rename limit now counts visible characters. Vanilla's 50 still applies,
<rainbow>Excalibur</rainbow> is9 characters rather than 28. A 256-character raw ceiling keeps a name from being
padded with unlimited markup. On 1.20.1 and 1.21.1 the text field rejects an
over-long name as you type; on 26.1 and 26.2 the server declines the rename,
because those versions removed the text-field filter hook.
- Removed** the
anvil.nameMaxLengthserver config option. It is replaced by the
mixin targeted
setItemName while the length constant lives in validateName.- The continuous message effects
breathe,drift,float,grow,rock,
spin and vibrate take three new timing params, so they can run in burstsinstead of looping forever:
every (seconds between bursts, 0 for continuous),hold (seconds a burst lasts) and delay (seconds before the first burst).Each burst restarts the effect's own clock at zero, so a sine-driven effect
enters and leaves at rest rather than snapping mid-swing. Existing messages are
unaffected;
every defaults to 0.<item>and<entity>now take hover and click from an enclosing<hover>or
<click> tag. The parser rebuilt icon spans from scratch and dropped everythingbut the icon's own attributes, so wrapping an icon in
<click> did nothing.- New
tooltipattribute on<item>and<entity>.tooltip=trueshows the
count andnbt; any other value is used as literal tooltip text.<hover>acceptsaction=show_itemandaction=show_entity, so ordinary text
- The hover region of an inline icon now matches the sprite. It was the icon's
tuck into surrounding text, leaving the right edge dead. Chat is now hit-tested
from the drawn sprite rather than the advance.
- Hover tooltips now work in mod GUIs, not just chat. Vanilla only hit-tests text
or any other custom UI had nothing to hook. Each interactive glyph's on-screen
rect is now recorded as it is drawn and tested against the cursor in any screen,
clipped to the active scissor so scrolled-out text is not hoverable. 1.20.1 and
1.21.1 only; 26.1 and 26.2 use a different text pipeline and are not covered yet.
- Fixed inline icons vanishing once they carried a hover or click event. Deriving
withHoverEvent/withClickEvent dropped the icon's registry id,so the tooltip appeared over empty space. This also restores icons combined with
<type> and <obfuscate>, which lost them the same way.- Fixed the hover and click region of an inline icon. Icons draw 16px wide but rode
measured them the same way.
## v3.0.3 (2026-08-10)
- A
<scroll>span is now a single unbreakable unit of layout. Multi-line scroll
from the per-frame
x progression, which restarts on every wrapped line, so itsized the whole marquee from the first line alone. A scroll span no longer wraps
and reserves only its viewport width. Text written after
</scroll> thereforestarts one viewport past the marquee rather than past the entire hidden string.
- New
[clamp]attribute andclampToScreen(boolean)builder method. Screen
partly off screen. Clamping also no longer inverts on a message larger than the
window, which used to snap it to the wrong corner.
- The anvil rename limit now counts visible characters. Vanilla's 50 still applies,
<rainbow>Excalibur</rainbow> is9 characters rather than 28. A 256-character raw ceiling keeps a name from being
padded with unlimited markup. On 1.20.1 and 1.21.1 the text field rejects an
over-long name as you type; on 26.1 and 26.2 the server declines the rename,
because those versions removed the text-field filter hook.
- Removed** the
anvil.nameMaxLengthserver config option. It is replaced by the
mixin targeted
setItemName while the length constant lives in validateName.- The continuous message effects
breathe,drift,float,grow,rock,
spin and vibrate take three new timing params, so they can run in burstsinstead of looping forever:
every (seconds between bursts, 0 for continuous),hold (seconds a burst lasts) and delay (seconds before the first burst).Each burst restarts the effect's own clock at zero, so a sine-driven effect
enters and leaves at rest rather than snapping mid-swing. Existing messages are
unaffected;
every defaults to 0.<item>and<entity>now take hover and click from an enclosing<hover>or
<click> tag. The parser rebuilt icon spans from scratch and dropped everythingbut the icon's own attributes, so wrapping an icon in
<click> did nothing.- New
tooltipattribute on<item>and<entity>.tooltip=trueshows the
count andnbt; any other value is used as literal tooltip text.<hover>acceptsaction=show_itemandaction=show_entity, so ordinary text
- The hover region of an inline icon now matches the sprite. It was the icon's
tuck into surrounding text, leaving the right edge dead. Chat is now hit-tested
from the drawn sprite rather than the advance.
- Hover tooltips now work in mod GUIs, not just chat. Vanilla only hit-tests text
or any other custom UI had nothing to hook. Each interactive glyph's on-screen
rect is now recorded as it is drawn and tested against the cursor in any screen,
clipped to the active scissor so scrolled-out text is not hoverable. 1.20.1 and
1.21.1 only; 26.1 and 26.2 use a different text pipeline and are not covered yet.
- Fixed inline icons vanishing once they carried a hover or click event. Deriving
withHoverEvent/withClickEvent dropped the icon's registry id,so the tooltip appeared over empty space. This also restores icons combined with
<type> and <obfuscate>, which lost them the same way.- Fixed the hover and click region of an inline icon. Icons draw 16px wide but rode
measured them the same way.
## v3.0.3 (2026-08-10)
- A
<scroll>span is now a single unbreakable unit of layout. Multi-line scroll
from the per-frame
x progression, which restarts on every wrapped line, so itsized the whole marquee from the first line alone. A scroll span no longer wraps
and reserves only its viewport width. Text written after
</scroll> thereforestarts one viewport past the marquee rather than past the entire hidden string.
- New
[clamp]attribute andclampToScreen(boolean)builder method. Screen
partly off screen. Clamping also no longer inverts on a message larger than the
window, which used to snap it to the wrong corner.
- The anvil rename limit now counts visible characters. Vanilla's 50 still applies,
<rainbow>Excalibur</rainbow> is9 characters rather than 28. A 256-character raw ceiling keeps a name from being
padded with unlimited markup. On 1.20.1 and 1.21.1 the text field rejects an
over-long name as you type; on 26.1 and 26.2 the server declines the rename,
because those versions removed the text-field filter hook.
- Removed** the
anvil.nameMaxLengthserver config option. It is replaced by the
mixin targeted
setItemName while the length constant lives in validateName.- The continuous message effects
breathe,drift,float,grow,rock,
spin and vibrate take three new timing params, so they can run in burstsinstead of looping forever:
every (seconds between bursts, 0 for continuous),hold (seconds a burst lasts) and delay (seconds before the first burst).Each burst restarts the effect's own clock at zero, so a sine-driven effect
enters and leaves at rest rather than snapping mid-swing. Existing messages are
unaffected;
every defaults to 0.<item>and<entity>now take hover and click from an enclosing<hover>or
<click> tag. The parser rebuilt icon spans from scratch and dropped everythingbut the icon's own attributes, so wrapping an icon in
<click> did nothing.- New
tooltipattribute on<item>and<entity>.tooltip=trueshows the
count andnbt; any other value is used as literal tooltip text.<hover>acceptsaction=show_itemandaction=show_entity, so ordinary text
- The hover region of an inline icon now matches the sprite. It was the icon's
tuck into surrounding text, leaving the right edge dead. Chat is now hit-tested
from the drawn sprite rather than the advance.
- Hover tooltips now work in mod GUIs, not just chat. Vanilla only hit-tests text
or any other custom UI had nothing to hook. Each interactive glyph's on-screen
rect is now recorded as it is drawn and tested against the cursor in any screen,
clipped to the active scissor so scrolled-out text is not hoverable. 1.20.1 and
1.21.1 only; 26.1 and 26.2 use a different text pipeline and are not covered yet.
- Fixed inline icons vanishing once they carried a hover or click event. Deriving
withHoverEvent/withClickEvent dropped the icon's registry id,so the tooltip appeared over empty space. This also restores icons combined with
<type> and <obfuscate>, which lost them the same way.- Fixed the hover and click region of an inline icon. Icons draw 16px wide but rode
measured them the same way.
## v3.0.2 — 2026-07-07
- The builder
effect(...)method now recognizes message effects and message
.effect("rock") or.effect("sound sound=...") from KubeJS (or Java) silently did nothingbecause the name was only looked up in the text-effect registry; it now routes
to the right registry like markup tags do.
- Text wrapping is on by default so long messages no longer run off screen.
scale. The new
[wrap] tag controls it: [wrap false] (or [wrap off])disables wrapping,
[wrap 200] sets an explicit width. The builder gainswrap(boolean) and wrap(boolean, int) alongside the existing wrap(int).- Wrapped and multi-line text now respects
align. Each line is centered (or
\nin a command now starts a new line, e.g.
/eta send @p 100 line one \n line two.## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.2 — 2026-07-07
- The builder
effect(...)method now recognizes message effects and message
.effect("rock") or.effect("sound sound=...") from KubeJS (or Java) silently did nothingbecause the name was only looked up in the text-effect registry; it now routes
to the right registry like markup tags do.
- Text wrapping is on by default so long messages no longer run off screen.
scale. The new
[wrap] tag controls it: [wrap false] (or [wrap off])disables wrapping,
[wrap 200] sets an explicit width. The builder gainswrap(boolean) and wrap(boolean, int) alongside the existing wrap(int).- Wrapped and multi-line text now respects
align. Each line is centered (or
\nin a command now starts a new line, e.g.
/eta send @p 100 line one \n line two.## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.2 — 2026-07-07
- The builder
effect(...)method now recognizes message effects and message
.effect("rock") or.effect("sound sound=...") from KubeJS (or Java) silently did nothingbecause the name was only looked up in the text-effect registry; it now routes
to the right registry like markup tags do.
- Text wrapping is on by default so long messages no longer run off screen.
scale. The new
[wrap] tag controls it: [wrap false] (or [wrap off])disables wrapping,
[wrap 200] sets an explicit width. The builder gainswrap(boolean) and wrap(boolean, int) alongside the existing wrap(int).- Wrapped and multi-line text now respects
align. Each line is centered (or
\nin a command now starts a new line, e.g.
/eta send @p 100 line one \n line two.## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.2 — 2026-07-07
- The builder
effect(...)method now recognizes message effects and message
.effect("rock") or.effect("sound sound=...") from KubeJS (or Java) silently did nothingbecause the name was only looked up in the text-effect registry; it now routes
to the right registry like markup tags do.
- Text wrapping is on by default so long messages no longer run off screen.
scale. The new
[wrap] tag controls it: [wrap false] (or [wrap off])disables wrapping,
[wrap 200] sets an explicit width. The builder gainswrap(boolean) and wrap(boolean, int) alongside the existing wrap(int).- Wrapped and multi-line text now respects
align. Each line is centered (or
\nin a command now starts a new line, e.g.
/eta send @p 100 line one \n line two.## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.2 — 2026-07-07
- The builder
effect(...)method now recognizes message effects and message
.effect("rock") or.effect("sound sound=...") from KubeJS (or Java) silently did nothingbecause the name was only looked up in the text-effect registry; it now routes
to the right registry like markup tags do.
- Text wrapping is on by default so long messages no longer run off screen.
scale. The new
[wrap] tag controls it: [wrap false] (or [wrap off])disables wrapping,
[wrap 200] sets an explicit width. The builder gainswrap(boolean) and wrap(boolean, int) alongside the existing wrap(int).- Wrapped and multi-line text now respects
align. Each line is centered (or
\nin a command now starts a new line, e.g.
/eta send @p 100 line one \n line two.## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.2 — 2026-07-07
- The builder
effect(...)method now recognizes message effects and message
.effect("rock") or.effect("sound sound=...") from KubeJS (or Java) silently did nothingbecause the name was only looked up in the text-effect registry; it now routes
to the right registry like markup tags do.
- Text wrapping is on by default so long messages no longer run off screen.
scale. The new
[wrap] tag controls it: [wrap false] (or [wrap off])disables wrapping,
[wrap 200] sets an explicit width. The builder gainswrap(boolean) and wrap(boolean, int) alongside the existing wrap(int).- Wrapped and multi-line text now respects
align. Each line is centered (or
\nin a command now starts a new line, e.g.
/eta send @p 100 line one \n line two.## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.2 — 2026-07-07
- The builder
effect(...)method now recognizes message effects and message
.effect("rock") or.effect("sound sound=...") from KubeJS (or Java) silently did nothingbecause the name was only looked up in the text-effect registry; it now routes
to the right registry like markup tags do.
- Text wrapping is on by default so long messages no longer run off screen.
scale. The new
[wrap] tag controls it: [wrap false] (or [wrap off])disables wrapping,
[wrap 200] sets an explicit width. The builder gainswrap(boolean) and wrap(boolean, int) alongside the existing wrap(int).- Wrapped and multi-line text now respects
align. Each line is centered (or
\nin a command now starts a new line, e.g.
/eta send @p 100 line one \n line two.## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.2 — 2026-07-07
- The builder
effect(...)method now recognizes message effects and message
.effect("rock") or.effect("sound sound=...") from KubeJS (or Java) silently did nothingbecause the name was only looked up in the text-effect registry; it now routes
to the right registry like markup tags do.
- Text wrapping is on by default so long messages no longer run off screen.
scale. The new
[wrap] tag controls it: [wrap false] (or [wrap off])disables wrapping,
[wrap 200] sets an explicit width. The builder gainswrap(boolean) and wrap(boolean, int) alongside the existing wrap(int).- Wrapped and multi-line text now respects
align. Each line is centered (or
\nin a command now starts a new line, e.g.
/eta send @p 100 line one \n line two.## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.2 — 2026-07-07
- The builder
effect(...)method now recognizes message effects and message
.effect("rock") or.effect("sound sound=...") from KubeJS (or Java) silently did nothingbecause the name was only looked up in the text-effect registry; it now routes
to the right registry like markup tags do.
- Text wrapping is on by default so long messages no longer run off screen.
scale. The new
[wrap] tag controls it: [wrap false] (or [wrap off])disables wrapping,
[wrap 200] sets an explicit width. The builder gainswrap(boolean) and wrap(boolean, int) alongside the existing wrap(int).- Wrapped and multi-line text now respects
align. Each line is centered (or
\nin a command now starts a new line, e.g.
/eta send @p 100 line one \n line two.## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.
## v3.0.1 — 2026-07-04
- Fixed a dedicated-server crash when a command built an immersive message,
/eta queue. ImmersiveMessagecarried its client-only rendering, so loading the class on a server threw
NoClassDefFoundError: MultiBufferSource. The rendering now lives in aclient-only holder, leaving the message data safe to load server-side. This
affected every loader and version; single-player was unaffected because the
client classes are present there.
- Unwrapped text now honors hard line breaks. Without
wrapset, a\nin the
breaks correctly whether or not wrapping is enabled.






