Practical guide
Create a movable information panel
Declare a panel contribution, query detached data, and compose a semantic movable panel without owning gameplay state.
Procedure
- Add an extend contribution targeting ui.surface.player-hud/panels to the package manifest.
- Implement the declared callback and request only the bounded detached records it needs through world.query.
- Open one panel with a package-stable ID, anchored inset geometry, movable enabled, and explicit semantic label and role.
- Append rows, progress indicators, text, and tooltips, then balance the block with ui.end_panel.
- Validate the package, reload at a frame boundary, and inspect package diagnostics if the prior graph remains active.
Example
function compose_resource_counts()
local result = world.query("world.resource.counts", {page_size = 16})
assert(result.status == "completed", result.detail)
ui.begin_panel({
id = "addon.resource-counts",
title = "Resources",
anchor = "bottom-left",
x = 24, y = 24, width = 280, height = 180,
movable = true,
semantic_label = "Current settlement resources",
semantic_role = "status"
})
for _, record in ipairs(result.records) do
ui.row(record.key, tostring(record.total))
end
ui.end_panel()
end
Constraints and recovery
- Panel IDs are package-scoped durable presentation identities. The host rejects duplicates inside one package and qualifies backend window state by package, so separate packages may reuse a local ID safely.
- Do not derive panel IDs from pointer values or transient record order; changing an ID intentionally creates a different presentation identity.
- A failed reload keeps the last valid composition graph active and records an error.package-reload diagnostic.
- Detached records may be retained as display values, but they do not grant authority over their source owners.