Session.idle ​

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 Session · inherited by testing.FakeSession · stable · since 2.0.0 · called as session.idle

ts
idle(options?: SignalOption): Promise<void>;

Resolves when nothing is open, queued, loading or animating, and the latest commit is shown: the frame that draws it has been drawn, so info().scene, the canvas or a screenshot read right after it shows that state. Open-ended motion (autoRotate, a clip played with loop: true, a continuous hook) doesn't count, nor does refinement that keeps improving a still frame afterwards (the soft ground shadow settling, a sharper still at rest). The only call that waits for the screen. Where that frame cannot come it ends without that frame: on a hidden page (a background tab), and while el is out of the viewport (the browser reports it, or no animation frame comes for about 2 s: a cross-origin iframe scrolled away draws none). The canvas and info().scene may then hold an older frame: for an image, capture()'s default source renders committed state off-display there too. Rejects AbortError when signal aborts. On the fake (/testing) time moves only on tick, so a bare await fake.idle() never settles: start it, then pump fake.tick(16) with an await between ticks until it settles, with a bound (a few hundred ticks; one is not always enough), as createFakeSession shows. What to wait on. Never a timer: a setTimeout or a debounce guesses, and a slow phone or a large GLB outlasts the guess. Each of these settles exactly when its write has landed:

Options ​

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

Abort (signal) ​

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

A wait (idle, capture): aborting rejects AbortError and nothing rolls back.

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/05-Session.idle.ts):

ts
import type { DeepReadonly, SceneModel, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
// What to wait on: the runtime says when a write has landed. No setTimeout, no debounce.
declare function showPrice(model: DeepReadonly<SceneModel>): void;
declare function setSaveEnabled(on: boolean): void;
// any commit, the shopper's, an undo's or yours, once it is shown: its event
session.on('scene-model:changed', ({ model }) => showPrice(model), { signal });
let latest: AbortController | null = null;
export async function pickFinish(material: string): Promise<void> {
  latest?.abort();                                   // newest wins, with no debounce
  const mine = (latest = new AbortController());
  setSaveEnabled(false);
  try {
    // one write: its own Promise, at its commit point, its loads included
    const r = await session.select('finish', material, {
      actor: 'shopper',                               // a shopper's pick
      origin: 'swatch',
      signal: mine.signal,
    });
    // 'refused' or 'rolled-back': the finish didn't change, and Save comes back below
    if (r.outcome === 'committed') await session.idle({ signal: mine.signal }); // drawn
  } catch (error) {
    // a newer pick aborted this one: still queued, it never lands; committed, it stops here
    if ((error as Error).name !== 'AbortError') throw error;
  } finally {
    if (latest === mine) setSaveEnabled(true);       // the newest pick settled, any outcome
  }
}

Generic-API test (spec 3.1) ​

Rule Result
1. Neutral name pass: idle names no product
2. A precedent pass: model-viewer (ar, camera-orbit, auto-rotate) (row G37); Unity Material.Lerp (row K17); WebGL KHR_parallel_shader_compile (row K35); Babylon.js Scene.whenReadyAsync (row K35)
3. Two apps, or labs pass: 28 apps (ledger: app-04c2774145, app-0c773a4ee3, app-37356bf09f, app-378d190f1b, app-4762caacdc, app-4f16d2f6da, app-4fd23bc869, app-510d750508 and 20 more; fleet cross-check: app-bcb7ef21da, app-e61a0b42ad, app-f3598fad77, app-fb16dd1985, app-fd09090420, configurator-template)
4. The cheaper fix first decided per need in Appendix B: rows G37, K17, K35 at tier stable (a member only where no rule, data, asset or app code serves the need)