Skip to content

CookBookManifest

Namespace Nfty.Core.Model in assembly Nfty.Ticket.

public record CookBookManifest : ISchemaVersioned

The top-level container: canvas, collection identity, and the per-Recipe roll weights.

Constructors

CookBookManifest(string, string, Dimensions, Collection, IReadOnlyDictionary<string, double>, int, int?, IReadOnlyList<string>?, IReadOnlyList<string>?, bool)

public CookBookManifest(string Id, string Name, Dimensions Canvas, Collection Collection, IReadOnlyDictionary<string, double> RecipeWeights, int SchemaVersion = 2, int? TargetSupply = null, IReadOnlyList<string>? Palette = null, IReadOnlyList<string>? RecipeOrder = null, bool PartialAlpha = false);

Parameters

  • Id (string) - Stable identifier, used as the archive's own name and in output metadata.
  • Name (string) - Display name.
  • Canvas (Dimensions) - The single source of truth for asset dimensions — every variant image in every nested Recipe is validated against it.
  • Collection (Collection) - Marketplace-facing name, description and symbol.
  • RecipeWeights (IReadOnlyDictionary<string, double>) - Roll weight per Recipe id. Zero shelves a Recipe without deleting it.
  • SchemaVersion (int) - The manifest format version; see Schema.
  • TargetSupply (int?) - How many assets this collection intends to mint, or null when the author has not committed to a number. Purely declarative: nothing in the generation pipeline reads it — generate takes its count from the caller — so setting it can never change what a cook produces. It exists so the book can state its intent, and so a UI can hold that intent up against UniqueSpace's count and say whether it is actually achievable. Added after schemaVersion 1 and therefore OPTIONAL with a null default — the pattern that let a v1 archive missing the field still read as valid back when v1 was still accepted (it is now refused outright; see UnsupportedSchemaVersionException.Require and Schema.Oldest). Schema.Current was deliberately NOT bumped for it: the change is purely additive, System.Text.Json ignores unknown properties so older builds read these archives fine, and a bump would instead make every already shipped build reject them.
  • Palette (IReadOnlyList<string>?) - The swatches this collection carries, as prefixed color specs (hex:d6249f), or null when the book has never saved one. Book-scoped so a collection's colors travel with it when it is handed to someone else — the app-wide palette in the .nfty store sits beneath these, see Palette.Combine. Specs rather than a color record because that is already how a manifest spells a color (ColorEntry.Fixed), so a palette reads the same as everything around it and ColorSpec stays the single parser. This is the ten-slot ramp's saved swatches only: the ramp itself is computed and the painting mode is editor state, so neither belongs in a file that describes a collection. Added after schemaVersion 1, and therefore OPTIONAL with a null default, for exactly the reasons spelled out for CookBookManifest.TargetSupply above — Schema.Current is deliberately NOT bumped.
  • RecipeOrder (IReadOnlyList<string>?) - The order the Recipes are listed in, by id, or null to list them ordinally by id — which is what every book written before this field did, and still does. It is PRESENTATION AND NOTHING ELSE, and that is worth being exact about, because the layer order one level down is the opposite. WeightedRoller.Prepare() sorts its keys with StringComparer.Ordinal before it builds the cumulative table, so which Recipe a given random number lands on cannot depend on the order they happen to be listed in — that ordinal sort is the same one that makes a seed reproducible across machine locales, and it makes this field unable to change a single asset. Reordering a Recipe therefore does NOT reroll the collection, and reordering a Recipe's LAYERS very much does. Unknown ids are ignored and unlisted Recipes sort after the listed ones, ordinally — so an entry left behind by a deleted Recipe is not an error and cannot hide one that is really there. This is the one place the file's order is allowed to be wrong without the book being wrong. Added after schemaVersion 1, and therefore OPTIONAL with a null default, for exactly the reasons spelled out for CookBookManifest.TargetSupply above — Schema.Current is deliberately NOT bumped.
  • PartialAlpha (bool) - Whether any variant under this manifest carries a partial-alpha pixel — the book-level value is what the UI calls "2D only". A listing cache: writers stamp it from the pixels on every write, and anything that decoded the pixels recomputes it rather than reading it.

Properties

Canvas

public Dimensions Canvas { get; init; }

The single source of truth for asset dimensions — every variant image in every nested Recipe is validated against it.

Collection

public Collection Collection { get; init; }

Marketplace-facing name, description and symbol.

Id

public string Id { get; init; }

Stable identifier, used as the archive's own name and in output metadata.

Name

public string Name { get; init; }

Display name.

Palette

public IReadOnlyList<string>? Palette { get; init; }

The swatches this collection carries, as prefixed color specs (hex:d6249f), or null when the book has never saved one. Book-scoped so a collection's colors travel with it when it is handed to someone else — the app-wide palette in the .nfty store sits beneath these, see Palette.Combine. Specs rather than a color record because that is already how a manifest spells a color (ColorEntry.Fixed), so a palette reads the same as everything around it and ColorSpec stays the single parser. This is the ten-slot ramp's saved swatches only: the ramp itself is computed and the painting mode is editor state, so neither belongs in a file that describes a collection. Added after schemaVersion 1, and therefore OPTIONAL with a null default, for exactly the reasons spelled out for CookBookManifest.TargetSupply above — Schema.Current is deliberately NOT bumped.

PartialAlpha

public bool PartialAlpha { get; init; }

Whether any variant under this manifest carries a partial-alpha pixel — the book-level value is what the UI calls "2D only". A listing cache: writers stamp it from the pixels on every write, and anything that decoded the pixels recomputes it rather than reading it.

RecipeOrder

public IReadOnlyList<string>? RecipeOrder { get; init; }

The order the Recipes are listed in, by id, or null to list them ordinally by id — which is what every book written before this field did, and still does. It is PRESENTATION AND NOTHING ELSE, and that is worth being exact about, because the layer order one level down is the opposite. WeightedRoller.Prepare() sorts its keys with StringComparer.Ordinal before it builds the cumulative table, so which Recipe a given random number lands on cannot depend on the order they happen to be listed in — that ordinal sort is the same one that makes a seed reproducible across machine locales, and it makes this field unable to change a single asset. Reordering a Recipe therefore does NOT reroll the collection, and reordering a Recipe's LAYERS very much does. Unknown ids are ignored and unlisted Recipes sort after the listed ones, ordinally — so an entry left behind by a deleted Recipe is not an error and cannot hide one that is really there. This is the one place the file's order is allowed to be wrong without the book being wrong. Added after schemaVersion 1, and therefore OPTIONAL with a null default, for exactly the reasons spelled out for CookBookManifest.TargetSupply above — Schema.Current is deliberately NOT bumped.

RecipeWeights

public IReadOnlyDictionary<string, double> RecipeWeights { get; init; }

Roll weight per Recipe id. Zero shelves a Recipe without deleting it.

SchemaVersion

public int SchemaVersion { get; init; }

The manifest format version; see Schema.

TargetSupply

public int? TargetSupply { get; init; }

How many assets this collection intends to mint, or null when the author has not committed to a number. Purely declarative: nothing in the generation pipeline reads it — generate takes its count from the caller — so setting it can never change what a cook produces. It exists so the book can state its intent, and so a UI can hold that intent up against UniqueSpace's count and say whether it is actually achievable. Added after schemaVersion 1 and therefore OPTIONAL with a null default — the pattern that let a v1 archive missing the field still read as valid back when v1 was still accepted (it is now refused outright; see UnsupportedSchemaVersionException.Require and Schema.Oldest). Schema.Current was deliberately NOT bumped for it: the change is purely additive, System.Text.Json ignores unknown properties so older builds read these archives fine, and a bump would instead make every already shipped build reject them.