# Package files

Every field of Style, Format, Manifest, and rapid.lock, with types and defaults.

Every field of the four package files, from the schemas in `contracts/src/` (`style.ts`, `format.ts`, `manifest.ts`). A field with a default may be left out.

Common types:

| Type | Is |
|---|---|
| hex | `#rrggbb` or `#rrggbbaa` |
| token | lowercase, starting with a letter, then `a-z`, `0-9`, `-`; up to 40 characters |
| name | lowercase, starting with a letter, then `a-z`, `0-9`, `-`; 2–48 characters |
| version | semantic: `1.2.3`, optionally with a pre-release, `1.2.3-beta.1` |
| sha256 | 64 lowercase hex digits |
| seconds | a finite number, 0 or more |

## Style

`style.json`. Schema version 1.

| Field | Type | Default |
|---|---|---|
| `schema_version` | `1` | required |
| `tokens` | Tokens | required |
| `motion` | Motion | all defaults |
| `sound` | Sound | all defaults |
| `components` | object: component name → object of parameters | `{}`; a place for a style to note values for agents. No code reads it, and it isn't the [`components/` folder](#the-components-folder) |
| `formats` | object: token (a format's name) → Adaptation | required; a format not listed isn't supported |
| `grid` | Grid | none |
| `video` | VideoType | none |

### The components folder

Apart from `style.json`, a style may ship `components/`: working scenes for the pieces its look needs that the kit doesn't have, one file each, with `components/README.md` listing them. A style can't carry scripts, so each piece's behaviour lives in a marked `<script>` block with its settings at the top. A builder copies that into a scene and tunes it, or writes a variation; the piece's look stays in `style.css`, and its pictures load as `rapid:style/<path>`. Render one to see it:

```sh
rapid still styles/bubble/components/<file> --style styles/bubble --t 1.8
```

See [making a style](/docs/making-a-style#6-components).

### Tokens

| Field | Type | Default |
|---|---|---|
| `palette` | object: token → hex | required; colours blend in OKLab |
| `fonts` | object: token (a role: `display`, `voice`, `ui`, …) → FontFace | required |
| `type_scale` | object: token → positive number | `{}`; pixels at the format's base width |
| `leading` | `{ min, max }`, positive numbers | `{ "min": 0.86, "max": 1.2 }` |
| `margins` | `{ x, y }`, numbers 0 or more | `{ "x": 72, "y": 72 }`; pixels at the base width |

### FontFace

| Field | Type | Default |
|---|---|---|
| `family` | string, 1–120 | required; as the system names it |
| `weight` | integer 100–1000 | `400` |
| `style` | `normal` or `italic` | `normal` |
| `substitutes` | array of strings | `[]`; families that may stand in, in order |

### Motion

| Field | Type | Default |
|---|---|---|
| `exit` | `off-edge`, `fade`, `cut`, or `scale` | `off-edge` |
| `easing` | object: token → string | `{}`; eases by role |
| `transitions` | array of strings | `[]`; a note of the hand-offs the look prefers, for agents to read. No code reads it, and its words needn't be the join's [named moves](/docs/transitions): the storyboard's `out` decides what's drawn |
| `pacing.line_hold` | seconds | `0.6`; how long a line holds after it lands |
| `pacing.shot` | positive number | `2.5`; typical shot length in seconds |
| `pacing.words_per_second` | positive number | `3.6`; for timing spoken lines |
| `pacing.chars_per_second` | positive number | `38`; for timing typed replies |

### Sound

| Field | Type | Default |
|---|---|---|
| `cues` | object: cue kind → instrument | `{}` |
| `colors` | object: token (a palette name) → instrument | `{}`; used when a cue carries a colour |
| `tempo` | positive number | none |

An instrument is one of the engine's voices: `kick`, `hat`, `pad`, `sub`, `keys`, `rim`, `room`,
`click`, or `whoosh`. A cue picks its instrument by its colour first, then its kind; a kind that is
itself an instrument's name plays it with no mapping. `rapid style check` refuses a style that
names any other instrument, and `rapid mix` reports the cues that played nothing and fails a video
whose cues all played nothing.

### Adaptation

| Field | Type | Default |
|---|---|---|
| `margins` | `{ x, y }` | the style's margins |
| `type_k` | positive number | none; a factor on the type scale |
| `grid` | Grid | the style's grid |

### Grid

The grid scenes align to, in pixels at the base width; scenes see it as `--rapid-grid-*`, and the `grid` check holds key-frame text to it.

| Field | Type | Default |
|---|---|---|
| `columns` | integer 1–24 | required; between the margins |
| `gutter` | number, 0 or more | required |
| `baseline` | positive number | none; text blocks start on its multiples from the top margin |

### VideoType

| Field | Type | Default |
|---|---|---|
| `steps` | object: token → positive number | `{}`; sizes in pixels at the base width, as `--rapid-video-<step>` |
| `hero_ratio` | number, 1 or more | `3`; the least the hero type may be over the body, for the `scale` check |

## Format

`format.json`. Schema version 1.

| Field | Type | Default |
|---|---|---|
| `schema_version` | `1` | required |
| `width`, `height` | positive even integers | required |
| `fps` | positive number, up to 120 | required |
| `length` | `{ min, max }`: seconds; `max` may be null for no limit | required |
| `guides` | array of Guide | `[]` |
| `min_text` | positive number | none; the smallest line height type may have, in pixels |
| `phone.max_bytes` | positive integer | `30000000` |
| `phone.shrink_after` | positive number | `120`; above this many seconds the phone copy drops to 720p on its short side |

### Guide

A named region of the frame that is never drawn; the checks hold elements to it by role.

| Field | Type | Default |
|---|---|---|
| `name` | string, 1–40 | required |
| `role` | `safe`, `keep-out`, `band`, `trim`, or `note` | required |
| `rect` | `{ x, y, w, h }` in frame pixels | required |
| `applies` | array of element roles (`type`, `object`, `card`, `bubble`, `texture`, `mark`, `other`) | `[]`: every role but `texture` |

`safe` is where elements must stay; `keep-out` is where they must not go; `band` is where type must sit. The `reel` format:

```json
{
  "schema_version": 1,
  "width": 1080,
  "height": 1920,
  "fps": 30,
  "min_text": 40,
  "length": { "min": 0, "max": null },
  "guides": [
    { "name": "safe", "role": "safe", "rect": { "x": 72, "y": 72, "w": 936, "h": 1776 }, "applies": [] },
    { "name": "band", "role": "band", "rect": { "x": 0, "y": 240, "w": 1080, "h": 1320 }, "applies": [] },
    { "name": "feed-buttons", "role": "keep-out", "rect": { "x": 960, "y": 900, "w": 120, "h": 750 }, "applies": ["type", "card", "bubble"] }
  ],
  "phone": { "max_bytes": 30000000, "shrink_after": 120 }
}
```

## Manifest

`manifest.json`, at the root of every style, format, or recipe.

| Field | Type | Default |
|---|---|---|
| `kind` | `style`, `format`, or `recipe` | required |
| `name` | name | required |
| `version` | version | required |
| `schema_version` | positive integer | required; the major version of the kind's schema the package was made for. A newer one than rapid knows is refused |
| `description` | string, 1–280 | required |
| `tags` | array of strings 1–32, at most 16 | `[]` |
| `formats` | array of names | `[]`; for a style, the formats it supports, each also in `style.json` |
| `fonts` | array of FontFace | `[]` |
| `files` | array of `{ path, sha256, bytes }` | required; every file but `manifest.json` |
| `proof` | Proof | none; required to share a style in rapid's repository |

A file's `path` is relative to the package root, with forward slashes, no leading `/`, no `..`, and no `\`. `rapid style check --write` fills `files` in.

### Proof

| Field | Type | Default |
|---|---|---|
| `scene` | string | required; the sample scene, `proof/scene.html`: an HTML scene that links `rapid:style` |
| `video` | string | required; the example video, at phone size |
| `poster` | string | required; the poster still |
| `golden` | array of `{ t, path }`, at least 1 | required; frames a new render of the sample must match (`rapid style proof --check`) |

Every proof path must be a file in the package. `rapid style proof` writes all of them and fills this in; see [making a style](/docs/making-a-style#8-prove-it).

### Rules

- Allowed file types: `.json`, `.css`, `.html`, `.md`, `.txt`, `.svg`, `.png`, `.jpg`, `.jpeg`, `.webp`, `.mp4`, `.wav`, and the fonts `.woff2`, `.woff`, `.ttf`, `.otf`.
- A font file sits in a folder with the full text of a free licence (`OFL.txt`, `LICENSE.txt`, `LICENCE.txt`, `LICENSE.md`, `LICENCE.md`, or `UFL.txt`).
- A style holds `style.css`, which styles every class of [the kit](/docs/styles-and-formats#the-kit).
- A stylesheet has no `@import`, and its `url()`s name only the package's own files.
- At most 25 MiB in all.
- Every file is listed, and every listed file is present with its size and sha256.
- A repository holding several packages lists them in an `index.json`: `{ "packages": [{ "name", "path", "kind"? }] }`.

## The lockfile

A project's pins, written by `rapid style use` and `rapid format use`.

| Field | Type |
|---|---|
| `lock_version` | `1` |
| `packages` | array of LockEntry, sorted by kind then name |

### LockEntry

| Field | Type | Means |
|---|---|---|
| `kind` | `style`, `format`, or `recipe` | |
| `name` | name | |
| `version` | version | |
| `sha256` | sha256 | of the package packed canonically (sorted entries, fixed stamps), not of the bytes as downloaded, so the same files pin the same hash from any source |
| `source` | string | where to fetch it again: an absolute path, an address, or a `github:` spec |

```json
{
  "lock_version": 1,
  "packages": [
    { "kind": "style", "name": "swiss", "version": "0.1.0", "sha256": "3a74…", "source": "/Users/you/code/rapid/styles/swiss" }
  ]
}
```
