Inline Style Parsers
This document explains how to register custom inline style parsers, allowing Ageratum's Markdown renderer to recognize and apply your mod's custom inline tags.
Overview
Inline Style Parsers let you extend Ageratum's inline Markdown syntax. By registering an MDInlineStyleParser, you can map specific tag pairs (e.g. <mytag>...</mytag>) to Minecraft Style objects, enabling custom colors, effects, or interactive behaviors.
Core Interface
MDInlineStyleParser
public interface MDInlineStyleParser {
/** Priority: lower value = higher precedence at the same position */
int priority();
/**
* Attempt to parse an inline style tag starting at pos in text.
* Returns null if no match is found.
*/
@Nullable
MDComponent.InlineStyleMatch parse(String text, int pos);
/** Create a simple open/close tag parser */
static MDInlineStyleParser create(
int priority,
Pattern openTagPattern,
String closeTag,
BiFunction<Style, Matcher, Style> styleFactory
) { ... }
}MDComponent.InlineStyleMatch
An InlineStyleMatch describes a successful tag match, containing:
- Start and end positions of the tag
- The
Styleto apply to the inner text - The inner text content
Registration Steps
1. Define the Parsers
package com.example.mymod.client.markdown;
import dev.anvilcraft.resource.ageratum.client.feat.markdown.component.MDInlineStyleParser;
import dev.anvilcraft.resource.ageratum.client.registries.AgeratumRegistries;
import net.minecraft.network.chat.Style;
import net.neoforged.neoforge.registries.DeferredHolder;
import net.neoforged.neoforge.registries.DeferredRegister;
import java.util.regex.Pattern;
public final class MyModInlineStyleParsers {
public static final DeferredRegister<MDInlineStyleParser> INLINE_STYLE_PARSERS =
DeferredRegister.create(
AgeratumRegistries.INLINE_STYLE_PARSER_REGISTRY_KEY,
"mymod"
);
// ── Example 1: <rainbow>text</rainbow> - Purple text ──────────────────────
public static final DeferredHolder<MDInlineStyleParser, MDInlineStyleParser> RAINBOW =
INLINE_STYLE_PARSERS.register(
"rainbow",
() -> MDInlineStyleParser.create(
100, // priority (lower = higher precedence)
Pattern.compile("<rainbow>"), // open tag pattern
"</rainbow>", // close tag string
(parentStyle, matcher) -> // returns modified Style
parentStyle.withColor(0xFF55FF).withBold(false)
)
);
// ── Example 2: <highlight=#RRGGBB>text</highlight> - Custom color text ────
private static final Pattern HIGHLIGHT_PATTERN =
Pattern.compile("<highlight=#([0-9a-fA-F]{6})>", Pattern.CASE_INSENSITIVE);
public static final DeferredHolder<MDInlineStyleParser, MDInlineStyleParser> HIGHLIGHT =
INLINE_STYLE_PARSERS.register(
"highlight",
() -> MDInlineStyleParser.create(
50,
HIGHLIGHT_PATTERN,
"</highlight>",
(parentStyle, matcher) -> {
int color = Integer.parseInt(matcher.group(1), 16);
// Note: Minecraft Style has no background color;
// this demonstrates colored + underlined text
return parentStyle.withColor(color).withUnderlined(true);
}
)
);
// ── Example 3: <spoiler>text</spoiler> - Obfuscated mask ──────────────────
public static final DeferredHolder<MDInlineStyleParser, MDInlineStyleParser> SPOILER =
INLINE_STYLE_PARSERS.register(
"spoiler",
() -> MDInlineStyleParser.create(
200,
Pattern.compile("<spoiler>"),
"</spoiler>",
(parentStyle, matcher) -> parentStyle.withObfuscated(true)
)
);
private MyModInlineStyleParsers() {}
}2. Bind to the Event Bus in Client Init
@Mod(value = "mymod", dist = Dist.CLIENT)
public class MyModClient {
public MyModClient(IEventBus modEventBus, ModContainer modContainer) {
MyModInlineStyleParsers.INLINE_STYLE_PARSERS.register(modEventBus);
}
}Using in Documents
Normal text <rainbow>This is colorful text</rainbow> normal text
<highlight=#FFAA00>This is highlighted text</highlight>
Spoiler: <spoiler>The answer is 42</spoiler>Priority Rules
When multiple parsers match at the same position, the one with the lower priority value is tried first.
If priorities are equal, registration order determines precedence.
Built-in parsers all use priority 0. Custom parsers are recommended to use the 50–200 range to avoid conflicting with built-ins.
Advanced: Fully Custom Parser
If the MDInlineStyleParser.create() factory method isn't sufficient (e.g. you need to parse nested content or multiple text segments), implement the full interface:
public class MyAdvancedParser implements MDInlineStyleParser {
private static final String OPEN = "<mymod>";
private static final String CLOSE = "</mymod>";
@Override
public int priority() { return 150; }
@Override
@Nullable
public MDComponent.InlineStyleMatch parse(String text, int pos) {
int start = text.indexOf(OPEN, pos);
if (start < 0) return null;
int contentStart = start + OPEN.length();
int end = text.indexOf(CLOSE, contentStart);
if (end < 0) return null;
// Build match using the static factory helper
Pattern openTag = Pattern.compile(Pattern.quote(OPEN));
Matcher m = openTag.matcher(text);
if (!m.find(start)) return null;
return MDComponent.InlineStyleMatch.of(
openTag, m, CLOSE,
(parentStyle, matcher) -> parentStyle.withColor(0x00FFFF)
);
}
}Note: See
MDComponent.javasource for the fullInlineStyleMatch.of()signature.
Interaction with Built-in Parsers
Ageratum's built-in inline parsers (color, obfuscated, hover, click) are all registered with priority 0.
To avoid tag name conflicts:
- Prefix your tag names with your mod ID (e.g.
<mymod_rainbow>) - Or choose names that don't conflict with built-ins:
color,o,hover,click