StatifierBlocks.ViewModel.Node (StatifierBlocks v0.27.0)

Copy Markdown View Source

One block, rendered: its resolved status, its presentation metadata, its slots (recursive), its form, and its own findings.

outcome is the outcome this block produces for the slot it sits in, when the parent's type declared a slot_outcome_key for that slot and this block's config holds a well-formed outcome name there; nil otherwise, which is every block whose parent declared nothing. It is resolved here rather than by a consumer so that the picture-drawing side reads one field instead of a metadata key plus a config lookup, and so it never learns that core.on_event is the type with an outcome.

join_label is what the join marker under a side-by-side arrangement reads: the string this block type's join_label callback returned for this block's config, normalized by StatifierBlocks.BlockType.join_label/2, and nil when the type declared none or the callback answered with something the refusal set rejects (ADR-0002 amendment B). It is resolved here for the same reason outcome is - the rendering side reads a string and never learns that core.parallel is the type with a completion rule.

It is derived for every node, including the ones whose slots stack and never draw a marker: a field that exists only sometimes is a field every consumer has to remember to guard, and entry.layout already says whether the marker is drawn. An unresolvable block reaches nil by the ordinary route rather than by a special case - its entry is the placeholder's, which declares no callback.

title is the author's own name for THIS block - the value of a declared :string field keyed label - and nil when the type declares no such field or the config holds nothing usable there, which is every block in the core.* vocabulary. nil rather than "the entry's label repeated" so that the card can tell the two apart: a block with a name of its own reads as its name over its type, and a block without one reads as its type alone rather than as its type printed twice (ViewModel.title/1 and ViewModel.subtitle/1 are that pair). It is derived here for the same reason outcome is - the rendering side reads a string and never learns which key an author's name lives under.

sentence is this block as one line of prose (ADR-0005's 2026-09-07 amendment): the string this block type's sentence/1 callback returned for this config where it declares one and the return is usable, else the author's own title where they gave one, else the type's label falling back to the type name. It is resolved by build/3 like every field beside it rather than being a function a consumer calls later, so a list view, an outline pane and a test read one field.

It is a fourth thing a node carries, not a re-spelling of title: ViewModel.title/1 keeps both its clauses and every consumer of it reads what it read before. It is not a chip - it is uncapped, appears in no chip list, and a card draws what it drew yesterday.

An unresolvable block has no callback to ask and no label, so it lands on the type name as the document stores it: a list view draws a line for every block in the document and never a blank one.

summary_titles is the raw text behind each chip in summary, nil where the chip is drawn as its type declared it (ADR-0005 decision 10w). It is index-aligned with summary by construction rather than by maintenance - StatifierBlocks.BlockType derives both from one pass over one callback - and it is read through ViewModel.summary_chip_titles/1, which realigns it against summary_chips/1 for a node built by hand.

invoke_type is what this block's config carries under invoke_type, when that is a non-empty string. It is a config key rather than a type name, so a host type that calls out to a handler gets the same third line core.invoke does by carrying the same key, and no component learns that core.invoke exists. Whether the value is WELL FORMED is validate_config/1's question and its answer arrives as a finding on the same card; hiding the string while a finding complains about it would be the card disagreeing with the form.

Summary

Types

status()

@type status() :: :ok | {:unresolvable, term()}

t()

@type t() :: %StatifierBlocks.ViewModel.Node{
  block_id: StatifierBlocks.Block.id(),
  entry: StatifierBlocks.BlockType.palette_entry(),
  findings: [StatifierBlocks.Finding.t()],
  findings_count: non_neg_integer(),
  form: StatifierBlocks.ViewModel.Form.t() | nil,
  invoke_type: String.t() | nil,
  join_label: String.t() | nil,
  outcome: String.t() | nil,
  raw_config_json: String.t() | nil,
  sentence: String.t() | nil,
  slots: [StatifierBlocks.ViewModel.Slot.t()],
  status: status(),
  summary: [String.t()],
  summary_titles: [String.t() | nil],
  title: String.t() | nil,
  type: StatifierBlocks.Block.type_name(),
  type_version: pos_integer()
}