# `MishkaCmsCore.Builder.Furnishing`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/builder/furnishing.ex#L5)

What a component says it should look like the moment it is dropped — declared by whoever wrote it,
rather than guessed by this CMS.

## Why this exists

`MishkaCmsCore.Runtime.PageBuilder.Starter` can furnish a dropped component without being told
anything: it reads Phoenix's own `inner_block` convention and the conventional slot names, and
falls back to a generic line. That works, and it works for a kit this install has never seen — but
it is inference. The word "alert" in a component's name is a guess about what the component means,
and nobody who ships a component library should have to name things this CMS's way to get a good
drop.

So a component may declare its own, and a declaration always wins:

    "extra": {
      "furnishing": {
        "body": "<tr><td>Ada Lovelace</td><td>Active</td></tr>",
        "slots": [
          {"name": "header", "body": "Name"},
          {"name": "header", "body": "Status"},
          {"name": "option", "body": "First choice", "attrs": {"value": "one"}}
        ]
      }
    }

Both keys are optional. `body` alone furnishes the content between the tags; `slots` alone
furnishes a component whose content is a list of entries; declaring `body: ""` explicitly says "put
nothing inside", which a spinner wants and the heuristics would otherwise override.

## Untrusted by construction

This is a third party's JSON, on the same footing as the bundle itself — see
`MishkaCmsCore.UiKit.Contract`, which is why anything malformed answers `:none` and the heuristics
take over, rather than raising into a palette render. A slot entry that fails validation is dropped
on its own; the rest of the furnishing stands.

Two things it deliberately does NOT do. It does not accept a function or a module reference: a
furnishing may travel through a `MishkaInstaller` hook payload, which is plain-map only — the
`MishkaCmsCore.Dashboard.Widget` rule. And it does not template anything; the strings are HEEx
fragments written verbatim, so a component that wants a random id asks for one through the ordinary
seeding path rather than through a placeholder syntax invented here.

# `t`

```elixir
@type t() :: %MishkaCmsCore.Builder.Furnishing{
  attrs: map() | nil,
  body: String.t() | nil,
  declares_body: boolean() | nil,
  slots: list() | nil
}
```

# `build`

```elixir
@spec build(term()) :: {:ok, t()} | :none
```

Reads a declared furnishing, or answers `:none` when there is none to read.

`:none` is not a failure — most components declare nothing, and the heuristics are the answer for
them. It is also what a malformed declaration gets, for the reason in the moduledoc.

# `builder`

# `empty?`

```elixir
@spec empty?(t()) :: boolean()
```

Whether a declaration says anything at all.

A furnishing that validated but carries neither a body nor a slot is treated as absent, so a kit
writing `"furnishing": {}` does not silently strip the content the heuristics would have given.

# `enforce_keys`

# `enforce_keys`

# `example`

A sample %MishkaCmsCore.Builder.Furnishing{} populated from defaults + type-based fallbacks.

# `keys`

# `keys`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
