registry
The single registry every editor surface is built from — builtin tabs and world panels alike. The engine's own tools register through the same public API a world uses. Two surfaces consume the registry: the dock service (`system_tools.module`) renders whatever panels are register…
editorRegistry
The single registry every editor surface is built from — builtin tabs and world panels alike. The engine's own tools register through the same public API a world uses. Two surfaces consume the registry: the dock service (system_tools.module) renders whatever panels are registered, sorted by order, and the top bar (EditorTopBar.component) builds its menu bar from the registered menus and each panel's menu/order. Both subscribe to onChanged so add/remove is live.
Registrations run through two paths, both ending in addPanel: builtin tabs register from modules.api.editor.system_tools.panels (the require IS the registration), and world panels register via an editorPanel asset whose onRegister(self) hook calls addPanel. State lives in a fixed _G slot so it survives hot-reload and edit↔play mode flips. A duplicate id replaces the prior registration, which is how a world overrides a builtin tab and how hot-reload re-registers cleanly.
Panels and menus
M.addPanel(spec) -> boolean— add (or replace) a dock panel.spec:id(required, unique),build(required,(state) -> widget),label?,menu?(default"window"),order?,refresh?,onCallback?,onMount?,tick?,float?.M.removePanel(id) -> boolean— remove a registered panel by id.M.addMenuItem(spec) -> boolean— add (or replace) a clickable action item that runsonClick, with no associated panel.spec:id(required),onClick(required),label?,menu?,order?.M.removeMenuItem(id) -> boolean— remove a registered action item.M.addMenu(spec) -> boolean— add (or replace) a top-level menu in the bar.spec:id(required),label?,order?.M.removeMenu(id) -> boolean— remove a top-level menu and cascade-drop every item/panel that targeted it.
Introspection and subscription
M.list() -> { panels, items, menus }— snapshot copies of current registrations for introspection.M.onChanged(fn) -> handle— subscribe to any add/remove mutation;fnruns with no args.M.offChanged(handle) -> boolean— unsubscribe.
Host accessors (dock service + top bar)
M.setDock(controller?)— publish (or clear withnil) the liveDockController.M.dock() -> DockController?— the live dock controller, ornilwhen no dock is mounted.M.panelsSorted() -> {spec}— live dock panels sorted by(order, id), includingbuild.M.menuModel() -> {menu}— the full top-bar menu model: every menu in bar order, each carrying its entries ({kind="panel", id, label}or{kind="action", id, label}) inorder.
The DockController type is { open, close, toggle, isOpen } plus optional getLayout / restoreLayout for layout persistence.
Usage
local editor = require("@builtin::modules.api.editor.registry")
editor.addMenu({ id = "mygame", label = "My Game", order = 70 })
editor.addPanel({
id = "my_tools", label = "My Tools", menu = "scene", order = 50,
build = function(state) return Z.lbl("hello") end,
})
editor.addMenuItem({ id = "act", menu = "scene", label = "Go",
onClick = function() end })
Scoped to this part · feeds back into the world's score.