Labs.mirror ​

Contract 2.0.0 · package sha256 a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e · generated from configurator-runtime.d.ts by services/onsite-scripter/scripts/build-contract-docs.mjs; edit the source, not this file.

Method of Labs · labs · since 2.0.0 · called as session.labs.mirror

ts
mirror(axis: 'x' | null, options: C & SignalOption & {
  owner: Collection;
}): Promise<Report>;

A geometry mirror of models and collections about the world plane x = 0. Lights that are collection items mirror with their collection; the stage's own rig, the ground and the environment don't. Reads return mirrored world coordinates. null removes the mirror. Look-only; owned by owner. Writes stay in placement space, before the mirror: the scene model's transforms, pose rules on a model root (node(id)) or a collection root, and an item's at (local to its parent), which the runtime then mirrors. So un-mirror a position you read (bounds(), a world pose, a connector, a pick) before you write it: negate x, swap a box's min.x and max.x as you negate them, and reflect a world pose's matrix before copying it back; then take a local at through the inverse of its parent's pose. The camera is not mirrored: camera.project takes a read's mirrored coordinates as they are, as do an overlay anchor's world at (no on), Frame.camera and every camera.* input. Frame.pose and a drag's poses (DragMove.pose, what constrain returns, the 'drag:*' events' pose) are in placement space, as the writes above, so a pose computed from DragMove.ray or a read is un-mirrored before constrain returns it. Faces keep their sides: front faces stay front (the runtime corrects the winding), so no material's side changes with the mirror.

Options ​

Through Session:

Option Type Default What it does
actor Actor the expectGroup's actor when one is given, else 'app' Who made the write.
dryRun boolean false Runs and reports, then rolls back by design ('dry-run').
expectGroup HistoryGroup undefined A guard, checked at the call: this write belongs to the undo group you are running.
expectRevision number undefined (no check) If-match on session.revision.
origin string null Your tag, echoed unchanged in Report.origin and cause.origin.
owner Collection —
signal AbortSignal undefined Cancels this call; the member's JSDoc says what an abort does.
supersede boolean false (the write is queued behind the transaction and applies after it closes) While a transaction is open: roll it back (it resolves 'superseded'); the writes queued behind it apply in call order, then this one, which supersedes an…
undo 'rebase' | (string & {}) a recorded-state change gets one step labelled 'Change'; a look-only write gets none; the session's first model (cause 'load') gets none, whatever the label: it is the floor of history An undo label makes this write one undo step.

Through Tx:

Option Type Default What it does
owner Collection —
signal AbortSignal undefined Cancels this call; the member's JSDoc says what an abort does.

Refused here (typed never, so passing one fails to compile): actor, checkpoint, dryRun, expectGroup, expectRevision, origin, supersede, undo.

Abort (signal) ​

This call is a write (SignalOption.signal, the kinds of call).

A write: before the commit point it rolls back and rejects AbortError; after it, no effect, except that a running camera move stops where the last frame drew it (CameraMoveOptions).

Through a tx the commit point is the transaction's: aborting a write still loading rejects AbortError and stages nothing (catch it: unobserved, it rolls the transaction back, spec section 6.1, O6); aborting one already staged unstages it, its paths keeping the latest other write to them as after a failed savepoint (O7), and its Promise stays resolved 'staged'.

Example ​

Imports: values (has, validate, the Holo*Error classes, the pure helpers) from @holostep-configurators/runtime-client, types from @holostep-configurators/runtime-client/contract, /math and /testing names from those entries; boot from the runtime module the root's loadRuntimeAtFloor resolves to. A type your page defines is your app's: declare it there, never import it from the package.

Compiled under the contract's strict settings (examples/declaration/20-Labs.mirror.ts):

ts
import type { Collection, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
declare const dresserId: string;
declare const room: Collection;
await session.labs.mirror('x', { owner: room });
// the dresser's bounds read mirrored; the wall's `at` is written before the mirror
const box = session.find({ node: dresserId, kind: 'mesh' }).bounds();
const x = box ? -box.center.x : 0;
await room.set({ items: [{ key: 'wall', kind: 'group', at: { position: { x, y: 0, z: 0 } } }] });

Generic-API test (spec 3.1) ​

Rule Result
1. Neutral name pass: mirror names no product
2. A precedent pass: glTF 2.0 transformations (a negative determinant reverses the winding) (row E3)
3. Two apps, or labs labs: it graduates when a second app uses it; apps today: app-0c773a4ee3, app-b362a81c85, app-c1384707fc
4. The cheaper fix first decided per need in Appendix B: rows E3, K31 at tier labs (a member only where no rule, data, asset or app code serves the need)