---
title: Stage
description: "Owns a pool of reusable desktop icons."
---

```tsx
import { Stage, Sprite } from 'filedom';

<Stage prebake={[{ icon: tileIcon, count: 16 }]}>
  <Sprite icon={tileIcon} at={{ x: 240, y: 180 }} />
</Stage>
```

Render Stage inside [StageProvider](/docs/reference/components/stage-provider). The provider must supply a park and a background colour before Stage mounts; without them Stage throws.

## Props

| Prop | Type | Default | Description |
| - | - | - | - |
| `children?` | `React.ReactNode` | - | Sprites and components that render sprites. |
| `onSettled?` | `(() => void)` | - | Called after current visible positions are acknowledged. Keep the callback stable. |
| `prebake?` | `readonly PrebakeEntry[]` | - | Faces to prepare at startup. Each entry is an IconDescriptor or { icon, count?, lowWater?, refillCount? }. Count is initial inventory, defaulting to 8. |
| `desktop?` | `StageDesktop` | - | Optional desktop adapter for tests. |
| `stackingSize?` | `{ width: number; height: number; }` | - | Visible width and height used to plan overlaps. Defaults to the provider icon size as a square. |

Prebake the faces a session will show later so play never waits on a cold icon.
Prebake sizes pooled piles only, meaning the interchangeable files a default
[Sprite](/docs/reference/components/sprite#where-a-sprites-file-comes-from) leases.
`static` and `onDemand` sprites own their files and need no entry.

Every face refills by default, whatever its initial count. `lowWater` and
`refillCount` each default to `Math.max(1, Math.floor(count / 3))`. After a
lease, Stage adds `refillCount` files when the remaining free count is strictly
below `lowWater`. A 12-file pile defaults to 4/4, a 6-file pile to 2/2, and a
pile of 1 to 3 files to 1/1. Bare descriptors start with 8 files and default to 2/2.

```tsx
<Stage prebake={[
  { icon: slashIcon, count: 12, lowWater: 5, refillCount: 4 },
  { icon: rareLetterIcon, count: 2 },
  { icon: cursorIcon, count: 1, lowWater: 0 },
]} />
```

`lowWater: 0` disables proactive refill. An empty pool still grows by
`refillCount` before serving a lease, and that lease does not trigger a second
refill. `count` and `lowWater` must be nonnegative safe integers and
`refillCount` a positive safe integer; anything else throws a `RangeError`.
Duplicate entries for one face take the largest count and the largest explicit
value for each refill setting, and omitted settings are derived from the merged
count. Stage reads `prebake` once, when it is created; later renders ignore it.

Free inventory includes files that are still warming. With a low threshold or a
small batch, a burst can run out of warm files, and Stage keeps the cold ones
hidden until they are ready. A larger reserve gives them more time to warm.

## Frequency helpers

`countFrequencies(iterable)` returns a `Map` of exact occurrences. Strings are
counted by Unicode code point, with no case folding or whitespace filtering.
Use it for known text or puzzle histograms.

`allocateFrequencies(weights, { budget, minCount?, overrides? })` divides an
exact total budget across the keys of a weight map. `minCount` defaults to 1.
Overrides are exact counts reserved within the budget; each must name a key in
`weights` and be at least `minCount`. Leftover files go to the largest
remainders, with insertion order breaking ties. A budget that cannot cover the
minimums and overrides throws a `RangeError`. Zero-weight keys get the minimum
while any positive weight remains; if every remaining weight is zero, the
remainder is split equally.

```tsx
import { allocateFrequencies, countFrequencies, glyphIcon, SHELL_GLYPH_WEIGHTS } from 'filedom';

const histogram = countFrequencies('hello world');
const counts = allocateFrequencies(
  new Map([...glyphCharacters].map(char => [char, SHELL_GLYPH_WEIGHTS[char] ?? 1])),
  { budget: 235, minCount: 1 },
);
const prebake = [...counts].map(([char, count]) => ({ icon: glyphIcon(char), count }));
```

`SHELL_GLYPH_WEIGHTS` favours path characters and lowercase shell output.
`ENGLISH_GLYPH_WEIGHTS` favours common English letters, weighting both cases
equally. Both are heuristic relative weights. Give unlisted characters a weight
of 1, edit the map, or pass counts from your own corpus as weights.

## Hide prepared sprites in place

`<Sprite concealed at={point} icon={icon} />` keeps a prepared file at its
requested position with its hidden flag set. Clearing `concealed` reveals it
after the position write. `onSettled` waits for both the visibility and the
position acknowledgements. A prepared file stays warm while concealed; a newly
minted file still warms at the park first. Readiness here means the commands
were acknowledged. It does not guarantee when Finder paints the icon.

## Sprites that own their files

Two kinds of sprite take no lease and need no `prebake` entry, because the stage
mints a file for each of them.

`<Sprite static />` owns one file from mount until it unmounts or changes icon.
The file goes through the same park, cover, warm-up and z ticket as a pooled
one. Static files are minted in the order the sprites registered, which is tree
order, so a block of overlapping static sprites stacks in the order it is written.

`<Sprite onDemand />` skips the park as well. The stage mints a name, creates
the file hidden at the sprite's position, and deletes it when the sprite hides
or unmounts. These sprites are not in the stage's z ledger because a newly
created file is always on top.

See [Sprite](/docs/reference/components/sprite#where-a-sprites-file-comes-from).
