# 5.11 Capture and export

> Contract 2.0.0 · package sha256 `a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e` · generated from `examples/section-5/5.11-capture-and-export.ts` by `services/onsite-scripter/scripts/build-contract-docs.mjs`; edit the source, not this file.

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.

```ts
import { HoloBusyError } from '@holostep-configurators/runtime-client';
import type { CameraView, Session } from '@holostep-configurators/runtime-client/contract';
declare const session: Session;
declare const signal: AbortSignal;
declare const showroomView: CameraView;
declare function uploadThumbnail(image: Blob, meta: { sceneHash: string; complete: boolean }): Promise<void>;
const spec = { view: showroomView, size: { width: 800, height: 800 }, pixelRatio: 1, background: 'transparent' } as const;
async function thumbnail(signal: AbortSignal) {
  try { return await session.capture(spec, { signal }); }
  catch (e) {
    if (!(e instanceof HoloBusyError)) throw e;          // a transaction holds the display
    await session.idle({ signal });                      // bounded by the signal
    return session.capture(spec, { signal });
  }
}
// A pick made just before 'Kosárba' may still be loading: wait for it, at most 5 s.
async function settle(ms: number): Promise<boolean> {
  try { await session.idle({ signal: AbortSignal.timeout(ms) }); return true; }
  catch (e) { if (e instanceof DOMException && e.name === 'AbortError') return false; throw e; }
}
// A product GLB that failed stays in the model, unloaded and without parts, and capture() still
// reports completed (6.4): send no picture then, so the shop shows its own fallback.
function productDrawn(): boolean {
  const model = session.getSceneModel();
  if (!model || session.find({ role: 'product' }).bounds() === null) return false;
  const inModel = new Set<string>();
  const walk = (nodes: NonNullable<typeof model>['root']): void => {
    for (const n of nodes) if (n.enabled) { inModel.add(n.nodeId); walk(n.children ?? []); }
  };
  walk(model.root);
  return !session.info().problems.some((p) => p.code === 'asset-failed' && p.asset === 'model'
    && p.node !== undefined && inModel.has(p.node) && !session.node(p.node).exists);
}
const fresh = await settle(5000);
if (productDrawn()) {
  const shot = await thumbnail(signal);
  await uploadThumbnail(shot.image, { sceneHash: shot.snapshot.sceneHash, complete: shot.completed && fresh });
}
```

Back to [the examples](README.md) · [llms.txt](../llms.txt)
