LDLib2 UI — Agent Guide
This document is for AI agents. It provides a structured workflow for building UIs with LDLib2. Follow the decision tree, use the minimal templates, then navigate to detailed pages as needed.
Step 1: Determine Language / Format
Ask the user which format they want. This determines syntax for all subsequent code.
| Format | When to use | Language-specific doc |
|---|---|---|
| Kotlin | Mod development with Kotlin DSL (most concise) | kotlin_support.md |
| Java | Mod development with Java | getting_start.md |
| KubeJS | Modpack scripting, no compilation needed | kjs_support.md |
| XML | Declarative UI structure, visual editing | xml.md + XSD schema |
XML note: XML defines only the UI tree and styles. You still need Java/Kotlin/KubeJS code to load the XML, do data bindings, and wrap it in a
ModularUI. Guide the agent to read the XSD file for available tags and attributes.
Step 2: Determine UI Type
flowchart TD
START["What does the user need?"] --> TYPE{UI Type?}
TYPE -->|"Display-only overlay,<br/>editor, tool window,<br/>no server data"| SCREEN["Client-only Screen"]
TYPE -->|"Needs server data sync<br/>(inventories, configs, etc.)"| MENU_TRIGGER{"Trigger method?"}
TYPE -->|"Always-on HUD overlay"| HUD["HUD Overlay"]
TYPE -->|"Recipe viewer display<br/>(JEI/REI/EMI)"| XEI["XEI Category"]
TYPE -->|"Just a reusable<br/>UI component/widget"| COMPONENT["UI Component"]
MENU_TRIGGER -->|"Right-click a block"| BLOCK["BlockUIMenuType"]
MENU_TRIGGER -->|"Right-click with item"| ITEM["HeldItemUIMenuType"]
MENU_TRIGGER -->|"Command / keybind / any"| PLAYER["PlayerUIMenuType"]
SCREEN --> RET_MUI["Return: ModularUI"]
BLOCK & ITEM & PLAYER --> RET_MUI
HUD --> RET_MUI
XEI --> RET_MUI
COMPONENT --> RET_EL["Return: UIElement"]
Key rule: If the UI needs Player data or server-side state, it must be a Menu-based UI with ModularUI.of(ui, player).
Step 3: Minimal Templates
Each template is a complete, runnable starting point. Pick the one matching Step 2.
Client-only Screen
ModularUI createUI() {
var root = new UIElement().addClass("panel_bg").addChildren(
new Label().setText("Hello")
);
return ModularUI.of(UI.of(root, StylesheetManager.INSTANCE.getStylesheetSafe(StylesheetManager.GDP)));
}
// Open: Minecraft.getInstance().setScreen(new ModularUIScreen(createUI(), Component.empty()));
Menu-based UI (Server-synced)
For Menu UIs, use the built-in factories. See factory.md for full details.
public class MyBlock extends Block implements BlockUIMenuType.BlockUI {
@Override
public ModularUI createUI(BlockUIMenuType.BlockUIHolder holder) {
var root = new UIElement().addClass("panel_bg").addChildren(
new Label().setText("Block UI"),
new InventorySlots()
);
return ModularUI.of(
UI.of(root, StylesheetManager.INSTANCE.getStylesheetSafe(StylesheetManager.GDP)),
holder.player
);
}
// Open: BlockUIMenuType.openUI((ServerPlayer) player, pos);
}
class MyBlock : Block(...), BlockUIMenuType.BlockUI {
override fun createUI(holder: BlockUIMenuType.BlockUIHolder): ModularUI {
val root = element({ cls = { +"panel_bg" } }) {
label({ text("Block UI") })
inventorySlots()
}
return ModularUI(UI.of(root, StylesheetManager.GDP), holder.player)
}
// Open: BlockUIMenuType.openUI(player as ServerPlayer, pos)
}
// startup_scripts/main.js — runs on both sides
LDLib2UI.block("mymod:my_block_ui", event => {
event.modularUI = ModularUI.of(UI.of(
new UIElement().addClass("panel_bg").addChildren(
new Label().setText("Block UI"),
new InventorySlots()
)
), event.player);
});
// server_scripts: LDLib2UIFactory.openBlockUI(event.player, event.block.pos, "mymod:my_block_ui");
Other triggers: Replace
BlockUIMenuTypewithHeldItemUIMenuType(item) orPlayerUIMenuType(arbitrary). See factory.md.
HUD Overlay
// Client-side only. Register in RegisterGuiLayersEvent.
var muiCache = Suppliers.memoize(() -> ModularUI.of(UI.of(
new UIElement().layout(l -> l.widthPercent(100).heightPercent(100).paddingAll(10))
)));
event.registerAboveAll(MyMod.id("my_hud"), (ModularHudLayer) muiCache::get);
See hud.md for details.
XML-based UI
<?xml version="1.0" encoding="UTF-8" ?>
<ldlib2-ui xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/Low-Drag-MC/LDLib2/refs/heads/1.21/ldlib2-ui.xsd">
<stylesheet location="ldlib2:lss/mc.lss"/>
<root class="panel_bg">
<label text="Hello from XML"/>
<button text="Click me"/>
</root>
</ldlib2-ui>
// Load and use
var xml = XmlUtils.loadXml(ResourceLocation.parse("mymod:my_ui.xml"));
var ui = UI.of(xml);
// Query elements: ui.select("#my_id"), ui.select(".my_class > button")
return ModularUI.of(ui, player); // or ModularUI.of(ui) for client-only
For XML auto-completion and validation, read the XSD schema to know all available tags, attributes, and structure.
Step 4: Data Binding Patterns
This is the most critical knowledge for producing correct UI code. There are 3 patterns:
Pattern A: Client-only (no server sync)
Use bindDataSource / bindObserver or Kotlin's dataSource / observer. No network involved.
// Consumer: display dynamic data
new Label().bindDataSource(SupplierDataSource.of(() -> Component.literal("Value: " + myVar)));
// Producer: react to user input
new TextField().bindObserver(value -> myVar = value);
Pattern B: Bidirectional sync (Menu UI)
Use DataBindingBuilder + .bind(). Server getter/setter only — client side is automatic.
new Switch().bind(DataBindingBuilder.bool(() -> serverBool, v -> serverBool = v).build());
new TextField().bind(DataBindingBuilder.string(() -> serverStr, v -> serverStr = v).build());
new ItemSlot().bind(itemHandler, 0); // inventory shorthand
new FluidSlot().bind(fluidTank, 0); // fluid shorthand
Kotlin shorthand: switch { bind(::serverBool) }, textField { bind(::serverStr) }
Pattern C: Server-to-client read-only
Use S2C variants. Client cannot modify the value.
Kotlin: label { bindS2C({ Component.literal(serverData) }) }
For complex bindings (custom types, RPCEvent, List sync, remote getter/setter): read data_bindings.md.
Step 5: Navigation Map
Use this table to find detailed documentation by need.
Core Concepts
| Need | Read | Path |
|---|---|---|
| How ModularUI works | ModularUI | docs/ldlib2/ui/preliminary/modularui.md |
| Screen vs Menu | Screen & Menu | docs/ldlib2/ui/preliminary/screen_and_menu.md |
| UI Factories (Block/Item/Player) | Factory | docs/ldlib2/ui/factory.md |
| Layout (flexbox, grid, size, padding) | Layout | docs/ldlib2/ui/preliminary/layout.md |
| Stylesheet / LSS | Stylesheet | docs/ldlib2/ui/preliminary/stylesheet.md |
| Events (mouse, keyboard, lifecycle) | Events | docs/ldlib2/ui/preliminary/event.md |
| Data Bindings & RPC | Data Bindings | docs/ldlib2/ui/preliminary/data_bindings.md |
| Style Animation | Style Animation | docs/ldlib2/ui/preliminary/style_animation.md |
| HUD Overlays | HUD | docs/ldlib2/ui/hud.md |
| XEI (JEI/REI/EMI) | XEI | docs/ldlib2/ui/xei_support.md |
| XML UI | XML | docs/ldlib2/ui/xml.md |
| Kotlin DSL | Kotlin | docs/ldlib2/ui/kotlin_support.md |
| KubeJS bindings | KubeJS | docs/ldlib2/ui/kjs_support.md |
Components Quick Reference
| Component | Class | Key method | Doc |
|---|---|---|---|
| Base element | UIElement |
.layout(), .style(), .addClass() |
element.md |
| Label | Label |
.setText(), .bind() |
label.md |
| Button | Button |
.setText(), .setOnClick(), .setOnServerClick() |
button.md |
| Text field | TextField |
.setText(), .bind(), .setNumbersOnlyInt() |
text-field.md |
| Text area | TextArea |
.setText(), .bind() |
text-area.md |
| Toggle | Toggle |
.setText(), .bind() |
toggle.md |
| Switch | Switch |
.bind() |
switch.md |
| Selector | Selector |
.setCandidates(), .bind() |
selector.md |
| Progress bar | ProgressBar |
.setProgress(), .bind(), .label() |
progress-bar.md |
| Scroller | Scroller / Scroller.Horizontal |
.bind() |
scroller.md |
| Item slot | ItemSlot |
.bind(handler, slot), .setItem() |
item-slot.md |
| Fluid slot | FluidSlot |
.bind(tank, slot), .setFluid() |
fluid-slot.md |
| Inventory slots | InventorySlots |
(auto player inventory) | inventory-slots.md |
| Tab / TabView | Tab, TabView |
.addTab() |
tab.md, tab-view.md |
| Toggle group | ToggleGroup |
.addToggle() |
toggle-group.md |
| Scroller view | ScrollerView |
(scrollable container) | scroller-view.md |
| Split view | SplitView |
(resizable split) | split-view.md |
| Color selector | ColorSelector |
.bind() |
color-selector.md |
| Tag field | TagField |
.bind() |
tag-field.md |
| Search | SearchComponent |
.bind() |
search-component.md |
| Tree list | TreeList |
— | tree-list.md |
| Scene (3D) | Scene |
— | scene.md |
| Graph view | GraphView |
— | graph-view.md |
| Code editor | CodeEditor |
— | code-editor.md |
| Inspector | Inspector |
— | inspector.md |
| Template | Template |
(load from XML) | template.md |
| Bindable value | BindableValue<T> |
.bind() (hidden sync helper) |
bindable-value.md |
| Rich text | Text |
.setText() |
text.md |
Textures Quick Reference
| Texture | Class | Doc |
|---|---|---|
| Sprite (image) | SpriteTexture |
sprite.md |
| Color rectangle | ColorRectTexture |
color-rect.md |
| Color border | ColorBorderTexture |
color-border.md |
| SDF rectangle | SDFRectTexture |
sdf-rect.md |
| Resource rect | RectTexture |
rect.md |
| Item stack | ItemStackTexture |
item-stack.md |
| Fluid stack | FluidStackTexture |
fluid-stack.md |
| Text | TextTexture |
text.md |
| Animation | AnimationTexture |
animation.md |
| Group | GroupTexture |
group.md |
| Shader | ShaderTexture |
shader.md |
| UI Resource | UIResourceTexture |
ui-resource.md |
| LSS texture syntax | — | lss.md |
Step 6: Pre-completion Checklist
Before returning code to the user, verify:
- [ ] UI type matches need: Client-only Screen has no
playerparam; Menu UI hasplayerparam - [ ] Data bindings are correct: Server data uses
DataBindingBuilder.xxx().build()+.bind(); client-only data usesbindDataSource/bindObserver - [ ] Factory is registered: Menu UIs need a factory registration (Block/Item/Player) or manual MenuType
- [ ] KubeJS script placement:
LDLib2UI.*handlers must run on both sides — preferstartup_scripts/ - [ ] XML files are placed under
assets/<modid>/ui/and loaded viaResourceLocation - [ ] Stylesheet applied: Use
StylesheetManager.GDP,.MC, or.MODERNfor built-in themes, or provide custom LSS - [ ] Output format matches request: Return
ModularUIfor full UIs,UIElementfor reusable components