diff --git a/Dalamud/Game/Text/SeStringHandling/SeStringBuilder.cs b/Dalamud/Game/Text/SeStringHandling/SeStringBuilder.cs
new file mode 100644
index 000000000..1618687f0
--- /dev/null
+++ b/Dalamud/Game/Text/SeStringHandling/SeStringBuilder.cs
@@ -0,0 +1,200 @@
+using Dalamud.Game.Text.SeStringHandling.Payloads;
+
+namespace Dalamud.Game.Text.SeStringHandling
+{
+ ///
+ /// Helper class to build SeStrings using a builder pattern.
+ ///
+ public class SeStringBuilder
+ {
+ ///
+ /// Gets the built SeString.
+ ///
+ public SeString BuiltString { get; init; } = new SeString();
+
+ ///
+ /// Append another SeString to the builder.
+ ///
+ /// The SeString to append.
+ /// The current builder.
+ public SeStringBuilder Append(SeString toAppend)
+ {
+ this.BuiltString.Append(toAppend);
+ return this;
+ }
+
+ ///
+ /// Append raw text to the builder.
+ ///
+ /// The raw text.
+ /// The current builder.
+ public SeStringBuilder Append(string text) => this.AddText(text);
+
+ ///
+ /// Append raw text to the builder.
+ ///
+ /// The raw text.
+ /// The current builder.
+ public SeStringBuilder AddText(string text) => this.Add(new TextPayload(text));
+
+ ///
+ /// Start colored text in the current builder.
+ ///
+ /// The text color.
+ /// The current builder.
+ public SeStringBuilder AddUiForeground(ushort colorKey) => this.Add(new UIForegroundPayload(colorKey));
+
+ ///
+ /// Turn off a previous colored text.
+ ///
+ /// The current builder.
+ public SeStringBuilder AddUiForegroundOff() => this.Add(UIForegroundPayload.UIForegroundOff);
+
+ ///
+ /// Add colored text to the current builder.
+ ///
+ /// The raw text.
+ /// The text color.
+ /// The current builder.
+ public SeStringBuilder AddUiForeground(string text, ushort colorKey)
+ {
+ this.AddUiForeground(colorKey);
+ this.AddText(text);
+ return this.AddUiForegroundOff();
+ }
+
+ ///
+ /// Start an UiGlow in the current builder.
+ ///
+ /// The glow color.
+ /// The current builder.
+ public SeStringBuilder AddUiGlow(ushort colorKey) => this.Add(new UIGlowPayload(colorKey));
+
+ ///
+ /// Turn off a previous UiGlow.
+ ///
+ /// The current builder.
+ public SeStringBuilder AddUiGlowOff() => this.Add(UIGlowPayload.UIGlowOff);
+
+ ///
+ /// Add glowing text to the current builder.
+ ///
+ /// The raw text.
+ /// The glow color.
+ /// The current builder.
+ public SeStringBuilder AddUiGlow(string text, ushort colorKey)
+ {
+ this.AddUiGlow(colorKey);
+ this.AddText(text);
+ return this.AddUiGlowOff();
+ }
+
+ ///
+ /// Add an icon to the builder.
+ ///
+ /// The icon to add.
+ /// The current builder.
+ public SeStringBuilder AddIcon(BitmapFontIcon icon) => this.Add(new IconPayload(icon));
+
+ ///
+ /// Add an item link to the builder.
+ ///
+ /// The item ID.
+ /// Whether or not the item is high quality.
+ /// Override for the item's name.
+ /// The current builder.
+ public SeStringBuilder AddItemLink(uint itemId, bool isHq, string? itemNameOverride = null) =>
+ this.Add(new ItemPayload(itemId, isHq, itemNameOverride));
+
+ ///
+ /// Add italicized raw text to the builder.
+ ///
+ /// The raw text.
+ /// The current builder.
+ public SeStringBuilder AddItalics(string text)
+ {
+ this.Add(EmphasisItalicPayload.ItalicsOn);
+ this.AddText(text);
+ return this.Add(EmphasisItalicPayload.ItalicsOff);
+ }
+
+ ///
+ /// Turn italics on.
+ ///
+ /// The current builder.
+ public SeStringBuilder AddItalicsOn() => this.Add(EmphasisItalicPayload.ItalicsOn);
+
+ ///
+ /// Turn italics off.
+ ///
+ /// The current builder.
+ public SeStringBuilder AddItalicsOff() => this.Add(EmphasisItalicPayload.ItalicsOff);
+
+ ///
+ /// Add a map link payload to the builder.
+ ///
+ /// The id of the TerritoryType entry for this link.
+ /// The id of the Map entry for this link.
+ /// The internal raw x-coordinate for this link.
+ /// The internal raw y-coordinate for this link.
+ /// The current builder.
+ public SeStringBuilder AddMapLink(uint territoryTypeId, uint mapId, int rawX, int rawY) =>
+ this.Add(new MapLinkPayload(territoryTypeId, mapId, rawX, rawY));
+
+ ///
+ /// Add a map link payload to the builder.
+ ///
+ /// The id of the TerritoryType entry for this link.
+ /// The id of the Map entry for this link.
+ /// The human-readable x-coordinate for this link.
+ /// The human-readable y-coordinate for this link.
+ /// An optional offset to account for rounding and truncation errors; it is best to leave this untouched in most cases.
+ /// The current builder.
+ public SeStringBuilder AddMapLink(
+ uint territoryTypeId, uint mapId, float niceXCoord, float niceYCoord, float fudgeFactor = 0.05f) =>
+ this.Add(new MapLinkPayload(territoryTypeId, mapId, niceXCoord, niceYCoord, fudgeFactor));
+
+ ///
+ /// Add a quest link to the builder.
+ ///
+ /// The quest ID.
+ /// The current builder.
+ public SeStringBuilder AddQuestLink(uint questId) => this.Add(new QuestPayload(questId));
+
+ ///
+ /// Add a status effect link to the builder.
+ ///
+ /// The status effect ID.
+ /// The current builder.
+ public SeStringBuilder AddStatusLink(uint statusId) => this.Add(new StatusPayload(statusId));
+
+ ///
+ /// Add a payload to the builder.
+ ///
+ /// The payload to add.
+ /// The current builder.
+ public SeStringBuilder Add(Payload payload)
+ {
+ this.BuiltString.Payloads.Add(payload);
+ return this;
+ }
+
+ ///
+ /// Return the built string.
+ ///
+ /// The built string.
+ public SeString Build() => this.BuiltString;
+
+ ///
+ /// Encode the built string to bytes.
+ ///
+ /// The built string, encoded to UTF-8 bytes.
+ public byte[] Encode() => this.BuiltString.Encode();
+
+ ///
+ /// Return the text representation of this string.
+ ///
+ /// The text representation of this string.
+ public override string ToString() => this.BuiltString.ToString();
+ }
+}