Log inGet started
▣
module · drop-in viewer
asset⌬ modulemoduleprimary: init.luau·originates fromworld 07158574-5…

editor_ops

Delete and duplicate the current editor selection, each as one undo step. Delete despawns every selected entity and clears the selection; duplicate clones each selected entity via `entity.duplicate` — a full component, attribute, visual, and material clone that includes descendan…

by◐lumi·posted 2mo ago
What it does

editor_ops

Delete and duplicate the current editor selection, each as one undo step. Delete despawns every selected entity and clears the selection; duplicate clones each selected entity via entity.duplicate — a full component, attribute, visual, and material clone that includes descendants — and selects the copies. Both wrap the batch in a multiplayer undo operation so a single Ctrl+Z reverts the whole action. The editor scene entrypoint binds these to Delete and Ctrl+D.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

conforms to

zero/source-extract/v2

strict

bundleNameFor(entityName: string) → string

An asset name a selection root can mint: the entity's name reduced to the identifier characters asset names carry, deduped with a numeric suffix when the name is already taken.

argtypedescription
entityNamestring

parentIdOf(id: string) → string

The id of `id`'s parent, or nil at the world root. Guarded: a proxy whose entity went away between the read and the walk answers nil rather than raising into the caller's operation.

argtypedescription
idstring

selectionRoots(ids: { string }) → void

The ids in `ids` that none of the others contains — the selection's roots. An entity whose ancestor is also named travels with that ancestor, so an operation over a whole selection acts once per subtree instead of once per entity: grouping a parent and its child makes one child of the group, not two, and dragging both moves one thing. Ids naming no live entity are dropped. Order is the order given. @return The root ids.

argtypedescription
ids{ string }

canReparent(childId: string, parentId: string?) → void

Whether `childId` can take `parentId` as its parent, and why not when it cannot. A nil `parentId` is the world root, which anything with a parent can move to. A parent inside the child's own subtree would close a loop, and an entity is not its own parent. @return true, or false plus a reason: "entityMissing", "parentMissing", "selfParent", "wouldCycle" or "alreadyParent".

argtypedescription
childIdstring
parentIdstring?

reparent(ids: { string }, parentId: string?) → any

Reparent every root among `ids` under `parentId`, or to the world root when `parentId` is nil, as ONE undo step. Each entity keeps its world position, rotation and scale, so a reparent changes what an entity belongs to and not where it sits. An id the move cannot take is reported with its reason rather than skipped in silence. @return The operation's record.

argtypedescription
ids{ string }
parentIdstring?

groupSelection(name: string?) → any

Gather the selection under a new entity: one group at the centre of what it holds, taking every selection root, under whatever those roots already shared as a parent so the group stands where they stood. The group becomes the selection, which is what makes gather-then-move one gesture. @arg name The group's name; defaults to "Group". @return The operation's record, carrying the group's id in `created`.

argtypedescription
namestring?

bundleFromSelection( ) → any

Compose a bundle asset from each topmost selected entity: one bundle per selection root, named after the root, captured from its live hierarchy. Selects the new bundles in the asset scope. Returns the operation's record: one row per root with the bundle it minted, or why it could not.

deleteSelection( ) → any

Despawn every selected entity as one undoable operation, then clear the selection (the despawned ids are no longer valid selection members). Returns the operation's record: one entity row per selected ref, each carrying whether the entity is gone and, when it is not, why.

duplicateSelection( ) → any

Duplicate every selected entity via entity.duplicate (full component, attribute, visual, and material clone, including descendants) as one undoable operation, then select the copies. Returns the operation's record: `created` is the ids it made, in the order of the refs they came from, and each entity row names its source and the copy that source produced.

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

8items
▣
module · born here
❒asset
# 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. ```lua 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`, `command` or `select`. - `outcome` — `committed`, `partial`, `refused`, `cancelled` or `noop`. - `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 with `before`, `requested` and `after`, and its own `reason` when 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. `snapping` and `snapIncrement` say 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()`.
▲ 0↑ born
▣
module · born here
❒asset
# editorSelection Named selection scopes for the editor. A scope is an independently tracked, ordered set of typed refs plus a primary (the last ref added or clicked). Distinct scopes never clobber each other, so viewport, outliner, inspector and asset browser share one answer to "what is selected" per kind. A ref is `{ kind: string, id: string }` — a stable id, never a display string, so rename/move never invalidates a selection. ## Exports - `M.scope(name) -> Scope` — get/create a named scope handle. - `M.set(scope, refs)` — replace refs in order; primary becomes the last. - `M.get(scope) -> { Ref }` — refs in click order (fresh array). - `M.primary(scope) -> Ref?` — last-clicked ref, or nil. - `M.clear(scope)` — empty a scope. - `M.toggle(scope, ref)` — add if absent, remove if present. - `M.add(scope, refs)` — add each ref not already present; primary becomes the last added. - `M.remove(scope, refs)` — remove each of `refs`; primary becomes the last remaining or nil. - `M.contains(scope, ref) -> boolean` — membership by `(kind, id)`. - `M.count(scope) -> number` — the scope's selection size. - `M.subscribe(scope, fn) -> handle` / `M.unsubscribe(scope, handle) -> boolean`. - `M.context() -> { scope, refs, primary }` — last-focused scope, for command ctx. ## Usage ```luau local Selection = require("@builtin::modules.api.editor.selection") local scope = Selection.scope("entity") Selection.set(scope, { { kind = "entity", id = "player_1" } }) Selection.subscribe(scope, function() refreshInspector() end) ``` ## Notes - State lives in a fixed `_G` slot, seeded pre-seal by the boot chain (`prelude.luau`); it survives hot-reload and edit↔play flips. Mutations after boot write into nested tables only. - `set` / `toggle` / `clear` mark their scope as last-focused, which drives `context()` and therefore which scope commands act on. - The service holds the editor's selection state in Luau. Refs handed back by `get` / `primary` / `context` are copies, so a caller can hold or mutate them without touching internal state.
▲ 0↑ born

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported. This asset, its contents, and its direct deps are clean as of the latest commit.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.