editor_observe
What an editor action committed, and why it committed less than it was asked for.
editor_observe
What an editor action committed, and why it committed less than it was asked for.
Every editor action — a gizmo drag, a delete, a duplicate, a command dispatch, a selection gesture — closes by publishing one record here, and returns that same record to its caller. Nothing is measured per frame: a record is written where the work happens and read lazily.
local Obs = require("@builtin::modules.api.editor.editor_observe")
Obs.observe() -- the whole document: last of each kind, history, counts
Obs.last() -- the most recent action of any kind
Obs.last("drag") -- the most recent drag
Obs.history() -- every retained action, oldest first
The same reading is a tool: tools.use("editor", "observe").
What every record carries
action—drag,grab,delete,duplicate,commandorselect.outcome—committed,partial,refused,cancelledornoop.reason— the nearest cause, from the closed set below, when the outcome is anything but a clean commit.detail— the engine's own words for that cause.entities— one row per entity the action touched or tried to, each withbefore,requestedandafter, and its ownreasonwhen it is not a clean commit.committed/changed/refused— how many rows fall in each.seq,atMs,durationMs— which action this is and how long it was open.
The reason set
selectionEmpty, entityMissing, writeRefused, writeDiverged, pivotLost,
userCancelled, commitFailed, duplicateRefused, despawnRefused,
unchanged, commandMissing, commandDisabled, predicateRaised,
bodyRaised, hitNothing, pointerBlocked. Obs.REASONS maps each to its
one-line meaning, so a caller can enumerate the set rather than guess at it.
A drag that moved less than asked
The drag record separates the three quantities that are usually conflated:
pointerAsked— what the pointer's position asked for, before snapping.applied— what the gizmo handed the engine, after snapping.snappingandsnapIncrementsay why the two differ.- each entity's
after— the transform the engine holds, read back from the engine rather than recomputed from the drag's own arithmetic.
An entity whose after differs from its requested reads writeDiverged; one
whose write raised reads writeRefused with the message; one despawned mid-drag
reads entityMissing. A drag that ended on Esc reads cancelled /
userCancelled, and one whose selection emptied under it reads cancelled /
pivotLost — three terminal states a single "the object did not move" cannot
tell apart.
A drag that never began
A press that lands on a handle and starts no drag publishes a grab record
instead — Obs.last("grab"), or Gizmo.lastGrab(). pointerBlocked says the UI
layer held pointer focus, so the press never reached the handle. A press away
from every handle is a selection click rather than a grab, and records nothing.
One record per press: a frame that ticks the gizmo more than once reports the
press once.
unit names what pointerAsked and applied are measured in: metres for a
translate or plane drag (a world-space {x,y,z} delta), radians for a rotate
({radians}), factor for a scale ({factor}).
Per-frame cost
The editor's own per-frame work is named in the profiler rather than pooled into
lua_update: script.editor.gizmo.tick, script.editor.viewport.select,
script.editor.selection.highlight, and script.editor.panel.<id> for each
dock panel's rebuild. Read them with profiler.stats().
Scoped to this part · feeds back into the world's score.