UI Dashboard
The full fragment protocol in one shot: an app declares slots, mods fill them with fragments, and everything binds to live host data.
A fragment is a fill of a fragment-format slot, not a separate top-level mod concept. Mods contribute through a single fills object keyed by host slot id; a fragment fill carries markup, declared data bindings, and DOM event handlers. (The former top-level fragments[]/contributions shape still validates with a deprecation warning, but fills is the canonical surface.)
What It Shows
Section titled “What It Shows”- Host app with three slots (
sidebar.left,header.status,main.overlay) - Health panel mod that
fillsa slot, usingdata-bindfor values anddata-iffor conditional warnings - Inventory panel mod using the sandbox fragment API for iteration
- Fragment lifecycle with
hooks.fragment.updatehandlers - Cross-validation ensuring each fill targets a valid slot, matches its
acceptsformat, and (when the slot declares apayload) satisfies the slot’s payload schema
Running the Demo
Section titled “Running the Demo”node examples/ui-dashboard/src/demo.jsThe demo loads two mods, simulates game state changes (health decreasing, items being added), and prints the fragment HTML at each step.
App Manifest
Section titled “App Manifest”The host declares three slots:
{ "slots": [ { "id": "sidebar.left", "accepts": ["text/html"], "multiple": true, "style": "isolated" }, { "id": "header.status", "accepts": ["text/html"], "multiple": false, "style": "inherit" }, { "id": "main.overlay", "accepts": ["text/html"], "capability": "ui-mount", "multiple": true, "style": "scoped" } ]}Each slot’s accepts names the fragment formats it takes (text/html, text/html+jsml, application/jsml+json). A slot may also declare a payload (a full JSON Schema describing exactly what a valid fill looks like) and a reserved flag for surface declared ahead of any filler. The style modes (inherit, isolated, scoped) control how host styles reach the mounted fragment.
Health Panel Mod — Filling a Slot
Section titled “Health Panel Mod — Filling a Slot”The health-panel mod declares which slot it fills via its fills object, keyed by host slot id:
{ "name": "health-panel", "version": "1.0.0", "capabilities": ["ui-mount"], "entry": "src/mod.js", "fills": { "sidebar.left": [ { "id": "health-display", "format": "text/html", "source": "fragments/panel.html", "bindings": [ { "name": "health", "path": "player.health" }, { "name": "maxHealth", "path": "player.maxHealth" }, { "name": "name", "path": "player.name" } ], "priority": 10 } ] }}A fill can also declare a handlers array (entries shaped { selector, on, handler }) to wire DOM events to sandbox functions. (events is a deprecated alias for handlers; if both are present, handlers wins.) The DOM-handler handlers array is distinct from a host’s top-level events catalog: bindings are what you call, slots and handlers are what handles, events is what the host emits.
Health Panel Fragment
Section titled “Health Panel Fragment”The fragment uses data-bind to display values and data-if for conditional warnings:
<div class="health-panel"> <div class="player-name" data-bind="name">Unknown</div> <div class="health-bar"> <span data-bind="health">0</span>/<span data-bind="maxHealth">0</span> </div> <div data-if="health < 50" class="warning">Low health!</div> <div data-if="health < 20" class="critical">Critical — find a healer!</div></div>The mod script uses the sandbox fragment API for more complex updates:
hooks.fragment.update("health-display", function (bindings, fragment) { var pct = (bindings.health / bindings.maxHealth) * 100; var color = pct > 60 ? "green" : pct > 30 ? "yellow" : "red"; fragment.setAttr(".health-bar", "data-color", color); fragment.toggle(".warning", bindings.health < 50); fragment.toggle(".critical", bindings.health < 20);});Inventory Panel — Sandbox Iteration
Section titled “Inventory Panel — Sandbox Iteration”Since data-bind handles values and data-if handles visibility, iteration over arrays uses the sandbox fragment API:
hooks.fragment.update("inventory-list", function (bindings, fragment) { var items = bindings.inventory || []; var html = items.map(function (item) { return "<li>" + item.name + " (x" + item.count + ")</li>"; }); fragment.replaceChildren(".item-list", html); fragment.toggle(".empty-message", items.length === 0);});Loading Mods
Section titled “Loading Mods”The host loads mods using runtime.loadMod(), passing the mod manifest (with its fills) and the file sources it references:
const healthMod = runtime.loadMod(modManifest, { fragmentSources: { "fragments/panel.html": panelHtml, "src/mod.js": modScript, },});Fragment sources are provided as a Record<string, string>; the runtime sanitizes them on load, cross-validates each fill against the host’s slots, and resolves the fills into the slots they key.
What Happens at Each Step
Section titled “What Happens at Each Step”- Full health (80/100):
data-if="health < 50"evaluates tofalse— warnings hidden. Fragment API sets color to green. - After damage (40/100):
data-if="health < 50"flips totrue— warning shows. Color becomes yellow. - Critical (15/100): Both warnings visible. Color becomes red. New item appears in inventory via
replaceChildren.
See the source code for the complete example.