The full-width drawer row (ADR-0005, the 2026-08-29 shell amendment, rulings 1A and 2A).
1A says what belongs here and the test is two words: tabular and document-level. Content that is a grid of rows about the whole document goes in the drawer; content about one block does not, whatever its shape.
Two tabs ship. Truth tables were first. The document-level findings list joined them under operator ruling R4 (2026-08-29), which retired the text block that used to sit under the canvas: a list of findings is a grid of rows about the whole document, so 1A's test admits it and the canvas gets its height back. Fixture runs and the datamodel view have reserved places and are not drafted here.
The measurable reason the drawer exists at all: a truth table for a branch in a credit-card processing document is one row per case and one column per bound input plus the verdicts, and at the inspector's width it either scrolls sideways or inverts its column order to keep the answers on screen. The spike did the second and filed the inversion as a readability defect. A drawer is as wide as the editor is, which is the axis the table needs.
Never open-or-gone
2A: collapsed, the drawer is a strip carrying a title and a count - a small-caps label beside a chip, "Truth tables 3" - and that is what makes the content discoverable from any state rather than only from the affordance that opens it. Opening it with no table on the selected block shows the index page: the blocks that do own one, each of them a jump. That is the whole of the spike's cold-start gap, closed.
The strip reports the active tab, and an author who has not picked one
gets the first tab that actually holds something
(StatifierBlocks.Shell.drawer_view/1 resolves it). That is 2A's own
reasoning applied to a second tab: a strip is worth having because it says
what is in the drawer, and one that names an empty tab while the other has
four rows in it says the opposite.
The count is the document's, not the selection's. A strip reading 0
because nothing happens to be selected would tell an author something false
about their document.
A host's own tab
8A's split is that the package ships the editing surface and the host
ships what surrounds it. A host tab is that reaching the drawer: the
editor's drawer_tabs assign carries a label, a count and a function
component, the function is called here when its tab is the active one,
and the package learns nothing about what it draws.
This admits no new package tab. 1A's test - tabular, and about the whole document - governs the tabs shipped here, unchanged and unweakened; for a tab the host contributes, that test transfers to the host as its own obligation. The 2026-08-30 amendment recording the drawer's tab strip as a host seam is where that split is written down.
It is called the way HEEx calls <.tab /> rather than by applying it to a
bare map, which is StatifierBlocks.Editor.Icons' rule (sb-b8g) and the
same one for the same reason: a host component that derives a value with
assign/3, which is what an ordinary component does, raises on an assigns
map with no change-tracking key in it.
A function rather than a slot, for the reason StatifierBlocks.Editor's
own moduledoc gives: the first thing a host wants a tab for is a feed of
something happening now, and a slot body reading a host assign does not
redraw when that assign moves, because the editor is a LiveComponent and
a slot body is not one of the assigns it was passed. A pushed descriptor
is.
The resize is a command, not a hook
Decision 7 ships exactly one JavaScript hook and this section adds none.
The resize is a native range control inside a phx-change form: the new
height crosses as an ordinary event, StatifierBlocks.Shell.clamp_height/1
bounds it, and the host stores it - one round trip, and the state that
matters lives where state already lives. 2A is explicit that the height is
remembered per viewer and that the package has no viewer, so nothing
here persists anything.
Summary
Functions
The drawer row: a strip when collapsed, tabs and a table when open.
Functions
The drawer row: a strip when collapsed, tabs and a table when open.
Attributes
view(:map) (required) -StatifierBlocks.Shell.drawer_view/1's value.height(:float) (required)root(StatifierBlocks.ViewModel.Node) (required)target(:any) (required)class(:string) - Defaults tonil.host_tabs(:list) - The host's tab descriptors, already throughStatifierBlocks.Shell.host_tabs/1. The active one'scontentis called for the panel; the strip draws them all from@view.tabs, where their labels and counts already are.Defaults to
[].