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
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);
}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-onlyFor 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 shorthandKotlin shorthand: switch { bind(::serverBool) }, textField { bind(::serverStr) }
Pattern C: Server-to-client read-only
Use S2C variants. Client cannot modify the value.
new Label().bind(DataBindingBuilder.componentS2C(() -> Component.literal(serverData)).build());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