Skip to content
filedom
Esc
↑↓navigate↵open⌘Jpreview
On this page

Stage

Owns a pool of reusable desktop icons.

import { Stage, Sprite } from 'filedom';

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

Render Stage inside StageProvider. The provider must supply a park and a background colour before Stage mounts; without them Stage throws.

Props

PropType
children?React.ReactNode

Sprites and components that render sprites.

TypeReact.ReactNode
onSettled?(() => void)

Called after current visible positions are acknowledged. Keep the callback stable.

Type(() => void)
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.

Typereadonly PrebakeEntry[]
desktop?StageDesktop

Optional desktop adapter for tests.

TypeStageDesktop
stackingSize?{ width: number; height: number; }

Visible width and height used to plan overlaps. Defaults to the provider icon size as a square.

Type{ width: number; height: number; }

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

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

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.

Was this page helpful?