# UiScreenDescription

> Talos next · Since 0.6

A whole screen, as `ctx.ui.screen` takes it.

## Signature

```ts
export interface UiScreenDescription {
  /** The contract version. Always 1. */
  format: 1
  /** The screen's name, as `push`, `replace` and `GET /ui/screens` use it. */
  screen: string
  root: UiNode
}
```

Import: `import type { UiScreenDescription } from "@argus/api"`

## Members

| Member | Signature | Description |
|---|---|---|
| `format` | `format: 1` | The contract version. Always 1. |
| `screen` | `screen: string` | The screen's name, as `push`, `replace` and `GET /ui/screens` use it. |
| `root` | `root: UiNode` |  |

## Example

The tested fixture `fixtures/recipes/game-over-restart/scenes/behaviors/game-over-restart.ts`.

```ts
import type { Behavior } from "@argus/api"

/**
 * A round that ends on a results screen, whose Retry button restarts the scene.
 *
 * Space scores until the clock runs out; then the results go up with the score written into them.
 *
 * Two rules decide the shape. `push`'s props are the *reason* a screen went up, not what it
 * draws — a screen is described once, at load — so the values it shows are written after the
 * push with `ctx.ui.set("<screen>:<id>", "text", …)`. And `ctx.session.restart()` rebuilds the
 * scene at the end of the tick: every behaviour's state, the HUD and the screen stack go back to
 * how the scene starts, so nothing here has to be reset by hand.
 */
interface State {
  score: number
  left: number
  over: boolean
}

interface Props {
  /** Length of a round in seconds. Default 5. */
  roundSeconds?: number
}

const RESULTS = "results"

export default {
  init(ctx) {
    if (typeof ctx.state.score !== "number") ctx.state.score = 0
    if (typeof ctx.state.left !== "number") ctx.state.left = ctx.props.roundSeconds ?? 5
    if (typeof ctx.state.over !== "boolean") ctx.state.over = false
    ctx.events.on("ui-activated", (event) => {
      if (event.data.screen === RESULTS && event.data.id === "retry" && ctx.state.over) ctx.session.restart()
    })
  },

  update(ctx, dt) {
    if (ctx.state.over) return
    if (ctx.input !== null && ctx.input.pressed("score")) ctx.state.score += 1
    ctx.state.left = Math.max(0, ctx.state.left - dt)
    ctx.hud.score(ctx.state.score)
    ctx.hud.timer(ctx.state.left)
    if (ctx.state.left > 0) return

    ctx.state.over = true
    ctx.ui.push(RESULTS, { reason: "round-over" })
    ctx.ui.set("results:score", "text", `${ctx.state.score}`)
    ctx.events.emit("round-over", { score: ctx.state.score })
  },
} satisfies Behavior<State, Props>
```
