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
children?React.ReactNode
Sprites and components that render sprites.
React.ReactNodeonSettled?(() => void)
Called after current visible positions are acknowledged. Keep the callback stable.
(() => 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.
readonly PrebakeEntry[]desktop?StageDesktop
Optional desktop adapter for tests.
StageDesktopstackingSize?{ width: number; height: number; }
Visible width and height used to plan overlaps. Defaults to the provider icon size as a square.
{ 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.