MishkaCmsCore.Runtime.PageBuilder.Marketplace behaviour (Mishka CMS Core v0.0.1)

Copy Markdown View Source

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.

Summary

Functions

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

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

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

Generates the export file for format from tokens.

Installs a preset or a package.

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

Registry packages available to install.

Page presets available to install.

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.

Saved snippets.

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

Callbacks

install(atom, t, arg3, arg4)

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

packages(map)

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

presets(map)

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

snippets(map)

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

tokens(arg1)

Functions

adapter()

@spec adapter() :: module()

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

categories(tokens)

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

formats()

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

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

generate(atom, tokens)

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(kind, id, site_id \\ nil, actor \\ nil)

@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(filters \\ %{})

@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(filters \\ %{})

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

Registry packages available to install.

presets(filters \\ %{})

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

Page presets available to install.

reference(atom, token)

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(filters \\ %{})

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

Saved snippets.

tokens(site_id \\ nil)

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