Groups API

Groups combine blocks into one transformable hierarchy while preserving each child's editable content. Group mutations are command-backed and undoable.

Create and dissolve groups

const groupId = engine.block.group([shapeId, textId]);
const releasedIds = engine.block.ungroup(groupId);

group calculates the union of member bounds, inserts the group under the first valid member's parent, and converts children to group-local transforms. ungroup restores absolute transforms and z-order. Calling group([]) throws because no group can be created.

Pass sibling blocks that share one parent to group. Initial grouping does not compose transforms from different parent hierarchies and can move such members.

Change membership

engine.block.addToGroup(groupId, badgeId);
engine.block.removeFromGroup(groupId, textId);
engine.block.refitGroupBounds(groupId);

Adding and removing children preserves their world-space appearance by converting between page and group coordinates. refitGroupBounds fits a group to its current children without moving them visually and updates nested ancestor bounds. Invalid group or child targets are ignored.

Group context is interaction state, not document state. Entering or exiting a group is not added to undo history.

engine.block.enterGroup(groupId);
engine.block.enterGroup(nestedGroupId);

const stack = engine.block.getGroupContext();
// [groupId, nestedGroupId] — outermost first

engine.block.exitGroup();

const unsubscribe = engine.block.onGroupContextChanged((nextStack) => {
  console.log("Active group path", nextStack);
});

getGroupContext returns a copy. Entering the group already at the top of the stack and exiting at the scene root are no-ops.

Selection and z-order

engine.block.select(groupId);
engine.block.bringToFront(textId);
engine.block.sendBackward(shapeId);

Z-order methods synchronize child ordering inside groups as well as pages. Selection changes do not emit group-context changes.

History behavior

Grouping, ungrouping, changing membership, and refitting bounds each create an undoable command. Use a batch when several operations should undo together:

engine.beginBatch();
engine.block.addToGroup(groupId, badgeId);
engine.block.refitGroupBounds(groupId);
engine.endBatch();

Deep duplication of a group hierarchy is not currently part of the documented duplicate contract. Duplicate individual blocks when a copied hierarchy is required.

Next steps