# `Frame`

> 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.

Interface · `import type { Frame } from '@holostep-configurators/runtime-client/contract';` · stable · since 2.0.0 · section 15. Frame writer

The synchronous per-frame writer for [`onFrame`](Session.onFrame.md) and drag `constrain`. Writes are transient: they
win over rules and collections, show in the frame being drawn, and are never stored, undone,
counted in `revision` or reported. A write holds until the same hook writes that target again,
releases it with `null`, or is removed (a drag's writes end with the drag); a removal shows from
the next frame, which the runtime draws ([`Session.onFrame`](Session.onFrame.md)). Frames the runtime
draws without running hooks (stills, accumulation samples) keep a held write. Inside `phase: 'late'`, reads
are this frame's (bones included). A write through a part that reads `exists: false` draws
nothing and records no Problem; through a [`node()`](Writer.node.md) handle it holds on that address and shows
from the first frame that draws the node. A [`material`](Frame.material.md) write on a [`Material`](Material.md) that is disposed
or no longer in the scene (a replaced GLB's authored one) draws nothing and records no Problem
either. On a group part, and over parts hung under a part
([`Placement.parent`](Placement.parent.md), an item's `parent`), [`pose`](Frame.pose.md) and [`visible`](Frame.visible.md) cascade as a rule's do (spec
section 6.10, R8): the pose moves the subtree, `false` hides all of it, and a descendant's own
`true`, a frame write's or a rule's, doesn't undo a `false` above it.

## Members

| Member | Kind | Summary |
| --- | --- | --- |
| <a id="number"></a>[`number`](Frame.number.md) | property | The frame's number; observed reads carry it as `frame`. |
| <a id="dtms"></a>[`dtMs`](Frame.dtMs.md) | property | Milliseconds since the previous drawn frame. |
| <a id="timems"></a>[`timeMs`](Frame.timeMs.md) | property | A steady clock, milliseconds; use it for paths and loops. |
| <a id="phase"></a>[`phase`](Frame.phase.md) | property | 'drag': the frame passed to a drag's `constrain`. |
| <a id="pose"></a>[`pose`](Frame.pose.md) | method | The part's local pose, as [`PartState.pose`](PartState.pose.md) (for a model root, [`node(id)`](Writer.node.md): its world pose), moving its subtree and the parts hung under it (see above). |
| <a id="visible"></a>[`visible`](Frame.visible.md) | method | `false` on a group part, or on a part with parts hung under it, hides them too (see above). |
| <a id="material"></a>[`material`](Frame.material.md) | method | Values that never need a new shader. |
| <a id="camera"></a>[`camera`](Frame.camera.md) | method | A transient camera view for the frame being drawn: no commit, no 'committed' event, no undo step. |
| <a id="lines"></a>[`lines`](Frame.lines.md) | method | A lines item's points, item-local metres. |
| <a id="text"></a>[`text`](Frame.text.md) | method | A label item's text, fitted inside its `size` ([`LabelSpec.text`](LabelSpec.text.md)). |

## Declaration

```ts
export interface Frame {
  readonly number: number;
  readonly dtMs: number;
  readonly timeMs: number;
  readonly phase: 'update' | 'late' | 'drag' | 'other';
  pose(target: Part | Parts, pose: PoseInput | null): void;
  visible(target: Part | Parts, visible: boolean | null): void;
  material(material: Material, values: {
    baseColor?: ColorValue;
    emissive?: ColorValue;
    emissiveStrength?: number;
    roughness?: number;
    metallic?: number;
    alpha?: number;
  } | null): void;
  camera(view: ((CameraView | LookAtView) & {
    fovDeg?: number;
  }) | null): void;
  lines(part: Part, points: readonly Point3[] | null): void;
  text(part: Part, text: string | null): void;
}
```

## Generic-API test (spec 3.1)

| Rule | Result |
| --- | --- |
| 1. Neutral name | pass: `Frame` names no product |
| 2. A precedent | not recorded: no ledger row (coverage.json) names this member or its owner |
| 3. Two apps, or labs | pass: 21 apps (fleet cross-check: app-04c2774145, app-0c773a4ee3, app-378d190f1b, app-4762caacdc, app-4f16d2f6da, app-510d750508, app-59c5b5751e, app-b362a81c85 and 13 more) |
| 4. The cheaper fix first | not recorded per member (Appendix B decides it per need) |
