StatifierBlocks.Editor.Inspector (StatifierBlocks v0.16.0)

Copy Markdown View Source

The tabbed inspector: Config, Findings, Condition (ADR-0005, the 2026-08-29 shell amendment, ruling 3A).

3A is one rule and a list, and the rule is the part that matters: the inspector is about the selected block, and anything about the document goes to the drawer. The list of three tabs follows from it rather than the other way round, which is why Datamodel and Fixtures - inspector tabs in the spike - are drawer tabs here. They were never about the selected block.

Two things a reader will look for and not find:

  • There is no fourth tab and no :if that would add one. A pane that is about the document has one place to go, and 3A exists so that "which inspector tab does this become" stops being asked.
  • The Findings tab is not the document-level findings panel decision 13 names. That panel still exists, still lists every finding in the document, and still lives beside the canvas. This tab is the selected block's own findings, which is the distinction the campaign-014 polish pass filed as sb-3l1 item a.

The Findings tab with nothing selected (sb-dbqq)

3A's rule decides what a tab is about; it does not say what a tab does when its subject is missing. With no selection the Findings tab has no block to be about, and what it did was render the same "select a block" line the Condition tab does - a chip-less tab beside a document with four things wrong with it.

So with node: nil the tab reads the document: the chip carries StatifierBlocks.Shell.findings_count/1 over the findings the editor passes in - the one number this package means by "the document's findings", the same one the drawer's strip and StatifierBlocks.Editor.findings_count/3 report - and the panel lists them grouped by block, each row selecting its block through the same select event the canvas and the drawer's list push.

This is the tab's empty state and not a fourth surface. The moment anything is selected the tab is that block's findings again, unchanged, and the document-level list an author navigates is still the drawer's (R4). What the inspector adds is an answer where there was a dead end: the count is not recomputed here, the list is not filtered here, and selecting a row hands the author back to the pane's real subject.

The Condition tab reads; it does not evaluate

It renders the per-arm predicator source the block carries, one entry per :expression field, and it evaluates nothing. Evaluation is StatifierBlocks.Predicates, it is server-side, and what it produces - a truth table - is tabular and document-level, so 1A puts it in the drawer. The tab that shows an author what a branch asks and the drawer that shows what it answers are different surfaces on purpose.

Editing a condition stays on the Config tab, where every other field is edited, so there is exactly one form in the editor and one place a draft can live (decision 9).

The pane header, and what its status says (parity item 1.1)

Above the tabs sits the pane's own header row: the name of the pane, and a right-aligned status. The status is the inspector's subject stated in words - the selected block's type label, or no selection - and it is there because 3A's rule ("the inspector is about the selected block") is invisible in an empty pane otherwise: three tabs with nothing under them do not say whether the author has selected nothing or selected something that has nothing to show.

It reads the type's label, not the block's id. A label is what the author picked the block by in the palette and what its card reads on the canvas, so the header names the block in the vocabulary the rest of the editor already uses. An unresolvable block reaches the placeholder entry by the ordinary route and falls back to its raw type name, which is the only thing about it that is still known.

The inspector folds too (ADR-0005, the 2026-08-30 shell amendment)

This pane used to say it had no collapse - that the palette's fold was the wide arrangement's answer to a cramped canvas, that one pane was enough of an answer, and that a second one would be a separate ruling. It is that separate ruling that has now been made: the 2026-08-30 amendment to the shell arrangement grants the inspector its own fold, and the paragraph above is superseded rather than merely out of date.

What it is, is the palette's fold on the other side of the canvas: collapsed in, "inspector-collapse" out, data-collapsed on the pane and data-inspector on the layout, and the width given back by one stylesheet rule. No hook, no client state, no fourth tab, and nothing about 3A changes - a folded pane is still about the selected block, it is just not on screen. The chevron points the way the press will MOVE the pane, which on this side is the mirror of the palette's.

The Config tab's two sections (parity item 1.9)

Under the tabs the Config tab is two labelled sections rather than a form dropped into a pane. Block states what the pane's subject IS - its type, its id, and the slot it sits in - and Configuration holds the form.

The Block section is the one that has to be there when nothing is selected, which is why its three rows render either way and read as a dash when they have no value. A section that appears and disappears with the selection teaches an author nothing about what the pane will show them; three rows that are always in the same place, sometimes empty, say what the inspector is about before they have selected anything.

The three rows are deliberately the three an author can act on. Type is the type's label, the same string the header status reads, so the pane names the block the way the palette and the canvas do. Id is the block id verbatim and set in mono, because it is the string that appears in a finding, in a provenance map and in a URL, and an author reading one of those needs to match it character for character. Slot is the slot's label - StatifierBlocks.Shell.slot_label/2's answer - because a card seen on its own does not say which arm of a branch it is in.

Under the three rows, and only for a block whose type did not resolve, the Block section holds that block's stored config as canonical JSON (campaign-017 ruling D4). It used to sit on the card, where it made the one broken block the widest thing in its lane; it is the same bytes in the same order, moved to the surface an author reaches by asking about that block in particular. It is read-only here for the reason the Config tab is empty there: nothing declares which of those values are editable, and a form over them would be invented rather than derived.

It is the only thing in this pane that wraps mid-token. A block id ellipsises and a condition source scrolls, because both are strings an author matches character by character and neither is long by design; a stored config carries whatever the host that wrote it carried, so the only two honest choices are wrapping anywhere and clipping bytes the author is here to read.

The Configuration section's empty state is a box, not the one-line sentence the other tabs use. It is the only empty state in the editor standing where a control would be, and an unboxed sentence in that position reads as a caption for the form below it rather than as the reason there is no form.

Summary

Functions

The three tabs and the panel of whichever one is showing.

Functions

inspector(assigns)

The three tabs and the panel of whichever one is showing.

Attributes

  • tab (:atom) (required)
  • collapsed (:boolean) - folded to a rail - the 2026-08-30 shell amendment's inspector fold. Defaults to false.
  • node (:any) - the selected ViewModel.Node, or nil. Defaults to nil.
  • slot_label (:any) - Shell.slot_label/2 for the selection - the slot it sits in, or nil. Defaults to nil.
  • pending (:list) - Defaults to [].
  • expression_component (:any) - Defaults to nil.
  • invoke_types (:list) - Passed through to StatifierBlocks.Editor.Field; see its moduledoc. Defaults to [].
  • path_candidates (:list) - Passed through to StatifierBlocks.Editor.Field; see its moduledoc. Defaults to [].
  • value_candidates (:map) - Passed through to StatifierBlocks.Editor.Field; see its moduledoc. Defaults to %{}.
  • target (:any) (required)
  • class (:string) - Defaults to nil.
  • document_findings (:list) - ViewModel.findings - what the Findings tab reads with no selection. Defaults to [].
  • orphan_findings (:list) - ViewModel.orphan_findings - the subset with no block to select. Defaults to [].
  • root (:any) - the view model's root ViewModel.Node, read only for group labels. Defaults to nil.