# `MishkaCmsCore.Runtime.PageBuilder.Marketplace`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/page_builder/marketplace.ex#L5)

Everything the Studio sheet shows — design tokens, presets, the package registry and snippets —
behind one behaviour, so the panel is written once and the source can change underneath it.

ADR-10. The default is `MishkaCmsCore.Runtime.PageBuilder.Marketplace.Kits`, which answers out of
what is really on this server: its tokens are the site's own stylesheet declarations, its packages
are the kit components on disk this site has NOT installed, and installing one runs the same
dependency closure the first-run wizard does.

It used to default to `Mock` — the design prototype's seed data — on the reasoning that a tab
rendering its real, empty answer looks broken rather than unfinished. That reasoning outlived its
truth. Three of the four questions now have real answers, so the sample catalogue was showing
invented packages with prices beside an Install button that refuses, and invented presets beside an
Apply that only flashes. An honest empty state is worth more than a convincing one that lies; the
Presets tab says what it is missing and why.

`Mock` is still here and still opt-in, because the prototype's seed data is what the design was
drawn against:

    config :mishka_cms_core, :builder_marketplace,
      MishkaCmsCore.Runtime.PageBuilder.Marketplace.Mock

A remote registry later is the same config change, not a UI change. The precedent is
`UiKitHandler.ensure_bundle!/1`, which already does local-first-then-fetch for the component
bundles.

    config :mishka_cms_core, :builder_marketplace, MyApp.RealMarketplace

## Token generators are real

The four export formats are not mock-ups. `generate/2` emits a genuine `tailwind.config.js`
theme.extend, a `:root` custom-property block, a W3C design-tokens JSON document and an Elixir
theme module — ported line-for-line from the prototype's own generators. That is what makes the
Studio's Copy button worth having before any backend exists.

# `install`

```elixir
@callback install(atom(), String.t(), String.t() | nil, map() | nil) ::
  {:ok, map()} | {:error, term()}
```

# `packages`

```elixir
@callback packages(map()) :: [map()]
```

# `presets`

```elixir
@callback presets(map()) :: [map()]
```

# `snippets`

```elixir
@callback snippets(map()) :: [map()]
```

# `tokens`

```elixir
@callback tokens(String.t() | nil) :: [
  MishkaCmsCore.Runtime.PageBuilder.Marketplace.Token.t()
]
```

# `adapter`

```elixir
@spec adapter() :: module()
```

The configured adapter — what is really on this server unless a host swaps it.

# `categories`

```elixir
@spec categories([MishkaCmsCore.Runtime.PageBuilder.Marketplace.Token.t()]) :: [
  String.t()
]
```

The token categories present in a token list, in display order.

# `formats`

```elixir
@spec formats() :: [{atom(), String.t(), String.t(), String.t()}]
```

The export formats the Studio offers: `{key, label, filename, hint}`.

# `generate`

```elixir
@spec generate(atom(), [MishkaCmsCore.Runtime.PageBuilder.Marketplace.Token.t()]) ::
  String.t()
```

Generates the export file for `format` from `tokens`.

Returns the file's text. Ported from the prototype's own generators, so what the Studio copies is
what the demo promised — a real config, not a screenshot of one.

# `install`

```elixir
@spec install(atom(), String.t(), String.t() | nil, map() | nil) ::
  {:ok, map()} | {:error, term()}
```

Installs a preset or a package.

`actor` is who pressed it, and every row the install writes is written as them — see
`MishkaCmsCore.Runtime.Handlers.UiKitHandler.install_kit!/2`. `nil` is a real answer: a caller
with no session behind it, such as a boot-time install, writes as the system, which is what this
did for everybody before.

WHICH SITE is not this argument's to decide on its own — see `install_scope/2` on the handler,
which derives it from the actor, and which the Visual Editor calls before it gets here.

# `package_categories`

```elixir
@spec package_categories(map()) :: [String.t()]
```

The categories the registry's packages actually fall into, in display order.

Asked of the adapter when it can answer — `Kits` reads the kit catalogue's own groups — and
derived from the packages themselves when it cannot, so an adapter written before this existed
still gets chips that match its data. The Studio used to draw a list written out by hand; four of
its six chips named categories no component has ever had, and picking one showed an empty registry
under the words "everything is already installed".

# `packages`

```elixir
@spec packages(map()) :: [map()]
```

Registry packages available to install.

# `presets`

```elixir
@spec presets(map()) :: [map()]
```

Page presets available to install.

# `reference`

```elixir
@spec reference(atom(), MishkaCmsCore.Runtime.PageBuilder.Marketplace.Token.t()) ::
  String.t()
```

How a token is referenced in a given format — what the Studio shows beside each swatch so an
author can paste it straight into the right place.

# `snippets`

```elixir
@spec snippets(map()) :: [map()]
```

Saved snippets.

# `tokens`

```elixir
@spec tokens(String.t() | nil) :: [
  MishkaCmsCore.Runtime.PageBuilder.Marketplace.Token.t()
]
```

The design tokens the ADAPTER offers — sample data unless a host swaps it.

---

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