mcbe-ts-ui
v1.0.2
Published
A TypeScript library for generating Minecraft Bedrock JSON UI files with a fluent builder API.
Downloads
310
Maintainers
Readme
mcbe-ts-ui
A TypeScript library for generating Minecraft Bedrock JSON UI files with a fluent builder API.
Overview
Writing Minecraft Bedrock JSON UI files by hand is tedious and error-prone. This library provides:
- Type-safe builders - Fluent API with full TypeScript intellisense
- Automatic compilation - Write
.tsfiles, get.jsonoutput - Clean organization - Source files in
scripts/ui/, generated files inui/__generated__/ - Automatic
_ui_defs.json- Updates the UI definitions file automatically
Installation
npm install mcbe-ts-uiQuick Start
1. Create a UI Source File
Create a file in scripts/ui/ (e.g., scripts/ui/my_hud.ts):
import { defineMain, panel, label, image } from "mcbe-ts-ui";
// Define child elements as variables
const title = label("title")
.text("Hello Minecraft!")
.color([1, 1, 0])
.shadow()
.anchor("center");
const icon = image("icon").texture("textures/ui/my_icon").size(16, 16);
// Export the main element
export default defineMain(
"my_hud",
panel("main").fullSize().anchor("center").controls(title, icon)
);2. Compile
npx mcbe-ts-uiThis will:
- Compile
scripts/ui/my_hud.ts→ui/__generated__/my_hud.json - Compile
scripts/ui/hud/health.ts→ui/__generated__/hud/health.json - Update
ui/_ui_defs.jsonto include all generated files
The compiler recursively scans scripts/ui/ and preserves your subdirectory structure in the output.
3. Generated Output
{
"namespace": "my_hud",
"main": {
"type": "panel",
"size": ["100%", "100%"],
"anchor_from": "center",
"anchor_to": "center",
"controls": [
{
"title": {
"type": "label",
"text": "Hello Minecraft!",
"color": [1, 1, 0],
"shadow": true,
"anchor_from": "center",
"anchor_to": "center"
}
},
{
"icon": {
"type": "image",
"texture": "textures/ui/my_icon",
"size": [16, 16]
}
}
]
}
}API Reference
Defining Namespaces
defineMain() - Simple (Recommended)
Most namespaces only need a main element. Use defineMain() for the cleanest syntax:
import { defineMain, panel, label } from "mcbe-ts-ui";
const title = label("title").text("Hello").color([1, 1, 0]);
export default defineMain(
"my_namespace",
panel("main").fullSize().controls(title)
);defineUI() - With Callback
Use when you need multiple top-level elements or complex logic:
import { defineUI, panel, label, ref } from "mcbe-ts-ui";
export default defineUI("my_namespace", (ns) => {
ns.add(panel("element_a").size(100, 100));
ns.add(panel("element_b").size(100, 100));
ns.add(
panel("main")
.fullSize()
.controls(
ref("a@my_namespace.element_a"),
ref("b@my_namespace.element_b")
)
);
});Adding Children with .controls()
The .controls() method accepts:
- ElementBuilder instances (recommended) - automatically embedded
- Control references - for external elements
- Inline objects - for overrides
// Using builders directly (cleanest)
const child1 = panel("child1").size(50, 50);
const child2 = label("child2").text("Hello");
panel("parent").controls(child1, child2);
// Using ref() for external elements
panel("parent").controls(
ref("[email protected]"),
ref("panel@other_namespace.panel")
);
// Inline with overrides
panel("parent").controls({ "[email protected]": { size: [100, 30] } });Cross-File References with nsRef()
Share elements across files without duplication:
// shared.ts
export const sharedButton = panel("action_btn")
.extends("common.button")
.size(100, 30);
// my_ui.ts
import { sharedButton } from "./shared";
import { defineMain, panel, nsRef } from "mcbe-ts-ui";
export default defineMain(
"my_ui",
panel("main")
.fullSize()
.controls(
nsRef("shared", sharedButton), // Basic reference
nsRef("shared", sharedButton, { offset: [0, 40] }) // With overrides
)
);
// Generates: { "[email protected]_btn": { offset: [0, 40] } }Element Builders
Panel
panel("name")
.size(100, 50) // Fixed size in pixels
.size("100%", "50%") // Percentage size
.fullSize() // 100% x 100%
.fitContent() // Size to children (100%c x 100%c)
.offset(10, 20) // Position offset
.anchor("center") // Both anchors
.layer(5) // Z-layer
.visible(true) // Visibility
.enabled(true) // Enabled state
.clipsChildren() // Clip overflow
.alpha(0.5) // Transparency
.controls(...) // Add childrenStack Panel
stackPanel("name")
.horizontal() // Horizontal layout
.vertical(); // Vertical layout (default)Label
label("name")
.text("Hello World") // Static text
.text("#binding_name") // Bound text
.color([1, 0.5, 0]) // RGB color
.shadow() // Drop shadow
.fontSize("large") // small/normal/large/extra_large
.fontScaleFactor(1.5) // Scale multiplier
.textAlignment("center") // left/center/right
.localize() // Enable localization
.enableProfanityFilter(false);Image
image("name")
.texture("textures/ui/my_image")
.uv(0, 0) // UV coordinates
.uvSize(16, 16) // UV size
.nineslice(4) // 9-slice border
.tiled() // Tile texture
.keepRatio() // Maintain aspect ratio
.color([1, 0, 0]) // Tint color
.grayscale(); // Grayscale filterButton
button("name")
.defaultControl("default_state")
.hoverControl("hover_state")
.pressedControl("pressed_state");Custom Renderer
custom("name").renderer("hover_text_renderer").renderer("paper_doll_renderer");Bindings
panel("name")
// Global binding
.globalBinding("#title_text", "#title_text", "none")
// View binding (computed expression)
.viewBinding("(not (#value = ''))", "#visible")
// Shorthand visibility/enabled
.visibilityBinding("(#is_visible)")
.enabledBinding("(#is_enabled)")
// Raw bindings array
.bindings(
{ binding_type: "global", binding_name: "#my_binding" },
{
binding_type: "view",
source_property_name: "...",
target_property_name: "...",
}
);Variables
panel("name")
.variable("my_color", [1, 0, 0])
.variable("offset", [10, 20])
.rawProp("$custom_var", "value"); // For arbitrary propertiesModifications (Vanilla UI)
// Using defineUI for modifications
export default defineUI("hud", (ns) => {
ns.addRaw("root_panel", {
modifications: [
{
array_name: "controls",
operation: "insert_back",
value: [{ "my_element@my_namespace.main": {} }],
},
],
});
});CLI Options
npx mcbe-ts-ui [options]
Options:
--source, -s <dir> Source directory (default: "scripts/ui")
--output, -o <dir> Output directory (default: "ui/__generated__")
--ui-defs, -d <file> UI defs path (default: "ui/_ui_defs.json")
--clean, -c Clean output directory first
--watch, -w Watch mode (not yet implemented)
--help, -h Show helpProject Structure
The compiler preserves your source directory structure. Files in subdirectories are output to matching subdirectories:
your-resource-pack/
├── ui/
│ ├── _ui_defs.json # Auto-updated
│ ├── _global_variables.json # Global vars (optional)
│ └── __generated__/ # Generated output (mirrors source structure)
│ ├── my_hud.json # from scripts/ui/my_hud.ts
│ └── hud/ # subdirectory preserved
│ ├── health.json # from scripts/ui/hud/health.ts
│ └── inventory.json # from scripts/ui/hud/inventory.ts
├── scripts/
│ └── ui/ # Your UI source files
│ ├── my_hud.ts
│ └── hud/ # Organize with subdirectories
│ ├── health.ts
│ └── inventory.ts
└── package.jsonOrganizing with Subdirectories
Use subdirectories to organize related UI components:
scripts/ui/
├── main_menu.ts → ui/__generated__/main_menu.json
├── hud/
│ ├── health.ts → ui/__generated__/hud/health.json
│ ├── hotbar.ts → ui/__generated__/hud/hotbar.json
│ └── minimap.ts → ui/__generated__/hud/minimap.json
└── screens/
├── inventory.ts → ui/__generated__/screens/inventory.json
└── settings.ts → ui/__generated__/screens/settings.jsonShared/Utility Files
Not every .ts file needs to export a UI definition. The compiler handles this gracefully:
- Files without a default export are scanned but skipped from JSON generation
- Use
_prefix for shared utility files as a naming convention (e.g.,_helpers.ts)
scripts/ui/
├── _shared.ts → (imported only - no JSON)
├── _constants.ts → (imported only - no JSON)
├── utils.ts → (imported only - no JSON)
├── my_hud.ts → ui/__generated__/my_hud.json
└── hud/
├── _helpers.ts → (imported only - no JSON)
└── health.ts → ui/__generated__/hud/health.jsonExample shared file:
// _helpers.ts - No default export, just shared components
import { panel, label } from "mcbe-ts-ui";
export const sharedHeader = panel("header").size("100%", 30);
export const sharedButton = panel("btn").extends("common.button").size(100, 30);Import and use in your UI files:
// my_hud.ts
import { defineMain, panel } from "mcbe-ts-ui";
import { sharedHeader } from "./_helpers";
export default defineMain(
"my_hud",
panel("main").fullSize().controls(sharedHeader)
);