Skip to content

Recipes & Customization

A recipe is an ordered list of token entries that controls what text appears in a node's description. The customization store manages active recipes per role and supports presets.

RecipeEntry

ts
interface RecipeEntry {
  token: TokenName;
  brevity: Brevity;
}
PropertyTypeRequiredDescription
tokenstringyesThe token name to include (e.g., 'name', 'visData').
brevity'short' | 'long'yesWhich form of the token value to use.

A recipe is simply RecipeEntry[]. The order of entries determines the order of text fragments in the assembled description.

Customization

ts
interface Customization {
  role: string;
  recipe: readonly RecipeEntry[];
}
PropertyTypeRequiredDescription
rolestringyesThe hyperedge role this customization applies to (e.g., 'root', 'xAxis', 'filteredData').
recipeRecipeEntry[]yesThe ordered token entries.

Customizations are automatically persisted to localStorage and restored on subsequent page loads.

CustomizationStore

The runtime creates a CustomizationStore that manages customizations per role:

ts
interface CustomizationStore {
  activeFor(role: string): Accessor<Customization>;
  setFor(role: string, customization: Customization): void;
  resetFor(role: string): void;
  registerPreset(name: string, customizations: readonly Customization[]): void;
  applyPreset(name: string): void;
  activePresetName(): string | null;
  listPresets(): ReadonlyArray<{ name: string; customizations: readonly Customization[] }>;
  setRecipeFilter(fn: RecipeFilter | null): void;
  recipeFilter(): RecipeFilter | null;
}

Key methods

MethodDescription
activeFor(role)Returns a reactive accessor for the active customization for a role.
setFor(role, customization)Sets a custom recipe for a role.
resetFor(role)Resets to the default recipe.
registerPreset(name, customizations)Adds a named preset.
applyPreset(name)Applies all customizations from a preset.
activePresetName()Returns the currently active preset name, or null.
listPresets()Returns all registered presets.
setRecipeFilter(fn)Sets a function that post-processes recipes (e.g., hiding the parent token when there are no multi-parent nodes).

Default recipe

When no customization or preset is active, the default recipe is:

ts
const DEFAULT_RECIPE: RecipeEntry[] = [
  { token: 'name', brevity: 'long' },
  { token: 'index', brevity: 'short' },
  { token: 'parent', brevity: 'long' },
  { token: 'children', brevity: 'short' },
];

Virtual parent-context nodes have their own default:

ts
const DEFAULT_VIRTUAL_RECIPE: RecipeEntry[] = [
  { token: 'parentContext', brevity: 'long' },
];

Recipe filters

The runtime installs a recipe filter that removes the parent token when no edges of the given role have multiple parents. This prevents announcing "parent: X" on nodes where there's only one possible parent — the information adds no value.

ts
type RecipeFilter = (role: string, recipe: readonly RecipeEntry[]) => readonly RecipeEntry[];

How recipes drive descriptions

When the describe function assembles a description for a nav node:

  1. It looks up the node's role (from edge.role).
  2. It calls customization.activeFor(role)() to get the current recipe.
  3. For each RecipeEntry in the recipe, it finds the corresponding token by name.
  4. It calls token.compute(context) and selects short or long based on entry.brevity.
  5. Non-empty results are assembled into a single string using join hints.

This means you can customize descriptions by:

  • Reordering entries in the recipe.
  • Changing brevity from 'long' to 'short' (or vice versa).
  • Adding or removing token entries.
  • Registering entirely new tokens and including them in a recipe.

Next

Released under the BSD-3-Clause License.