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

The render-time bridge between a compiled runtime template and a compiled runtime component.

Every module built by `MishkaCmsCore.Runtime.Compilers.ComponentCompiler` imports `component/1`
and `component/2` from here, so a template written as `<.component component_name="card" .../>`
resolves against an already-loaded module rather than through an `Code.eval_quoted/3`
interpretation. Which resolution it takes is the module's own answer: a function component is
`apply(CardModule, :card, [assigns])`, and one compiled stateful goes through
`Phoenix.Component.live_component/1` instead. The template is identical either way — see the fork
in `applied/3` for why the call site must not change.

`MishkaCmsCore.Runtime.StaticHelpers` is this module for the surfaces that render a template to a
string, where a live component has no diff to be turned into HTML by.

Slot items arriving from the database (`:runtime_slot`) are grouped by their `for_slot` name and
resolved against the target module's **declared** slots, so a name the component never declared
is reported as a slot error instead of silently minting an atom.

## Which site's components a render reaches

A global component (`site="Global"`, or no `site`) and a component of the site this process renders
for — the one `MishkaCmsCore.Runtime.Translations.put_site/1` named — and nothing else. A component
reads as its own site, so one named from another site's page would draw that site's template and
that site's rows to every visitor, and run its `handle_event`. It is refused before it is built and
nothing is drawn in its place. A process that names no site reaches the global ones only. The
module that answers is asked as well, since two names can hash to one module: it is drawn only
when it was built from a row of the site asked for, or from a global row.

The site is named where a request becomes a page — `MishkaCmsWeb.RuntimeLive`,
`MishkaCmsWeb.DraftPreviewLive`, `MishkaCmsWeb.ErrorHTML` — and by the builder's canvas; a preview
built for one site among others renders inside `rendering_for/2`. A global layout draws on the
page's site, so it reaches that site's components and the global ones.

## In a task

The site a template renders for and the response's CSP nonce are kept in the process rendering
it, as Gettext's locale is. A task the template starts through
`MishkaGervaz.Messages.start_async/4` — or `assign_async/4`, or `in_caller_locale/1` — is handed
all three (`carry_into_tasks/0`), so `t/1,2`, `site_path/2`, `component/1` and `csp_nonce/0`
answer inside it what they answer outside it:

    def load(socket) do
      MishkaGervaz.Messages.start_async(socket, :links, fn ->
        %{about: site_path("/:lang/about", "fa"), label: t("About")}
      end)
    end

A task started any other way, `Phoenix.LiveView.start_async/4` among them, renders for no site
and in the default locale: `site_path/2` is `nil` there, and `t/1` answers from the global words.

## Events

- `runtime:render:component_unavailable` — what is drawn in place of a refused component, an empty
  `%Phoenix.LiveView.Rendered{}`, with `%{component_name: name, site: site, site_id: site_id}` as
  its meta: the name and site the template asked for, and the site being rendered. A plugin may
  answer with markup of its own.
- `runtime:render:before_content_component` — before each component a `rich_content/1` marker
  names is drawn; see `MishkaCmsCore.Runtime.ContentComponents`.

See also `MishkaCmsCore.Runtime.Compilers.ComponentCompiler` for how the target modules and their
`__component_information__/0` metadata surface are generated.

# `captcha`

The captcha a form asks — see `MishkaCmsCore.Captcha.Widget.captcha/1`.

    <.captcha form={f} for="sign_in" tenant={assigns[:current_tenant]} />

# `carry_into_tasks`

```elixir
@spec carry_into_tasks() :: :ok
```

Registers what the imports above read from the process rendering a template — the site
`MishkaCmsCore.Runtime.Translations.put_site/1` names and the nonce
`MishkaCmsCore.Runtime.CspNonce.put/1` records — with
`MishkaGervaz.Messages.carry_process_keys/1`, so a task started through
`MishkaGervaz.Messages.start_async/4`, `assign_async/4` or `in_caller_locale/1` renders as its
starter does. Gervaz carries the locale itself. See "In a task" above.

Called once, when `MishkaCmsCore.Application` starts.

# `component`

Renders a runtime component by name.

Requires `:component_name`; `:site` selects the tenant-scoped module and may be `nil` for a
global component. Any other assign is forwarded to the component untouched.

A `:site` that is neither global nor the site this process renders for draws nothing and builds
nothing — see "Which site's components a render reaches" above.

A component whose `precompile` flag is unset is not built at boot, so it is built when a render
first needs it. The page or layout drawing it builds the components it reaches several at once
before it draws (`MishkaCmsCore.Runtime.ComponentPrefetch`), and one it could not tell it
reaches **is built here** — by the first render that names it. `MishkaCmsCore.Runtime.Handlers.ModuleCompilerHandler`
coordinates through its registry, so concurrent first renders of the same component wait on one
compile rather than racing, and a component whose template does not compile is remembered in
`MishkaCmsCore.Runtime.States.CompileFailureState` so the next render short-circuits to the
placeholder instead of retrying.

The component's CSS does not depend on this: `MishkaCmsCore.Runtime.Handlers.BootHandler` writes
a template file for every active component whether or not it compiles a module, and that file is
what the closure walk and Tailwind read. So a lazily-built component renders styled the first
time it is seen.

# `component`

Renders a runtime component from a name/site pair, merging any extra `assigns`.

# `csp_nonce`

```elixir
@spec csp_nonce() :: String.t() | nil
```

This response's Content-Security-Policy nonce, for the rare `<style>` a template cannot avoid —
a page, a layout and a component alike, since a component never sees the page's assigns.

    <style nonce={csp_nonce()}>…</style>

`nil` outside a response, which HEEx renders as no attribute. See
`MishkaCmsCore.Runtime.CspNonce` for where the value comes from.

# `ensure_component_loaded`

```elixir
@spec ensure_component_loaded(String.t(), String.t() | nil) :: boolean()
```

Builds the runtime module for `name`/`site` if it is not already loaded, and says whether it is.

`component/1` calls this itself. It is public for the callers that read a component's compiled
metadata *before* rendering it — the page builder canvas asks `__component_information__/1` which
attributes a component declares, and a component that has never been built declares nothing, so
every attribute an author set would be silently dropped from the preview.

# `imports`

```elixir
@spec imports([map()] | nil) :: keyword(non_neg_integer())
```

What a runtime template imports from here — `component/1,2`, `captcha/1`, `sign_in_providers/1`,
`rich_content/1`,
`t/1,2`, `csp_nonce/0` and `site_path/2` — less any name its own `helpers` already define.

Every compiler — component, page and layout — asks this rather than writing the list itself, so
the three cannot drift. The exception is not hypothetical: importing a `t/1` into a module that
defines one is a compile error, and a template that saved yesterday would stop saving today. Its
own function wins, as a local function would anyway.

# `rendering_for`

```elixir
@spec rendering_for(String.t() | nil, (-&gt; result)) :: result when result: term()
```

Runs `fun` rendering for the site named `site`, then puts back the site this process rendered for.

`site` is a name, as a `<.component site="…">` attribute carries one. `"Global"`, `nil` and a name
`SiteState` does not know render for no site, which reaches the global components alone.

For a process that renders one site's template and then another's — a preview built in the admin,
a palette card — where naming the site for the whole process would leave it pointing at whichever
was drawn last. `t/1` inside `fun` answers from the same site's words.

# `renders_here?`

```elixir
@spec renders_here?(String.t() | nil) :: boolean()
```

Whether a template rendered by this process may reach a component of the site named `site`.

`"Global"` and `nil` always may; a site name only when it is the site
`MishkaCmsCore.Runtime.Translations.put_site/1` named for this process. A name `SiteState` does
not know never may. Two ETS reads for a site name, none for a global one.

`component/1` and `static_component/1` ask it themselves. It is public for a caller that builds a
component before rendering it — the builder's canvas reads a component's declared attributes
first — so that a component the render would refuse is never built, and its row never read, on
the way there.

# `resolve_component`

```elixir
@spec resolve_component(String.t(), String.t() | nil) :: {:ok, module()} | :error
```

The module `component/1` draws for `name` of the site named `site`, built on first use — or
`:error` where it draws nothing: a name nothing answers to, an inactive component, another site's.

For a caller that must know before it draws, as `rich_content/1` does to cast a marker's
attributes to what the component declares.

# `rich_content`

```elixir
@spec rich_content(map()) :: Phoenix.LiveView.Rendered.t()
```

Stored HTML with the components its markers name drawn in place — a post's body, say.

    <.rich_content
      id={"#{@id}-body"}
      html={@body}
      site={site_name()}
      current_user={@current_user}
    />

  * `html` — sanitized HTML; it is drawn as it is given, markers aside.
  * `site` — the site's name, as `<.component site="…">` takes it. A marker names the site's own
    component, else the global one of that name. `nil` reaches the global ones alone.
  * `id` — what a stateful component's id starts with, `"content"` when not given; unique on the
    page when two of these draw content.
  * `current_user` — handed to a component that declares a `current_user` attribute.

A marker that names nothing this site may draw is drawn as nothing. See
`MishkaCmsCore.Runtime.ContentComponents` for the marker and its rules.

# `sign_in_providers`

The buttons of the services the site signs in with — see
`MishkaCmsCore.Accounts.SignInProvider.Widget.sign_in_providers/1`.

    <.sign_in_providers site={assigns[:current_site]} />

# `site_path`

```elixir
@spec site_path(String.t(), String.t() | map() | nil) :: String.t() | nil
```

The public address of a stored path on the site this page is rendered for, in a language or with
captures — `MishkaCmsCore.Runtime.Languages.url/3`, so a link is written once and follows the
site's URL mode:

    <.link navigate={site_path("/:lang/about", @page.language)}>About</.link>
    <a href={site_path("/:lang/blog/:slug", %{"lang" => @page.language, "slug" => @post.slug})}>

A language of `nil` is the site's primary one. `nil` when the path makes no address — a capture
it needs is missing, or the language is not one the site publishes in — which HEEx draws as no
`href` at all. The site is the one `MishkaCmsCore.Runtime.Translations.put_site/1` named for this
process, as for `component/1`.

# `static_component`

The same component, rendered as MARKUP rather than as something LiveView will run — what the
builder's canvas draws.

## Why the canvas cannot call `component/1`

`MishkaCmsCoreWeb.Live.Admin.Builder.CanvasLive.contain/3` forces every node's render to iodata on
purpose, so that a component's raise, throw or exit lands inside a rescue the editor owns rather
than taking an author's unsaved session with it. `Phoenix.LiveView.Engine` implements
`Phoenix.HTML.Safe` for `%Phoenix.LiveView.Component{}` only to raise — the two facts are
irreconcilable, and `contain/3` is the one that must win.

So a stateful component is rendered here the way LiveView would, minus the process: its `mount/1`
runs if it has one, the compiler's `update_many/1` carries the author's assigns across, and
`render/1` is called as the ordinary arity-1 function it is. Real markup, real size, real styling.

## What the author sees, and what they do not

`@myself` is nil, so `phx-target` renders away rather than pointing at a cid that does not exist —
which means nothing in the frame responds to a click. That is the honest picture: the canvas is
showing the component's COLD state, and the editor says so beside the selection rather than
leaving the reader to wonder why the dropdown will not open.

The socket built here carries `private[:runtime_cold]`, which is how a component that loads in the
background knows to read inline instead: there is no process to send a task's answer to. That is
the ONLY surface where it is set — a page's first HTTP render is not connected either, and a
component must not confuse the two.

A function component takes the same path it always did, and another site's component is refused
here exactly as `component/1` refuses it.

# `t`

```elixir
@spec t(term(), keyword() | map()) :: String.t()
```

`key` in the language this page is being answered in — the runtime counterpart of `gettext/1`,
for templates that live in the database and so can never reach a `.po` file.

    <h2>{t("Categories")}</h2>
    <p>{t("Read %{count} posts", count: @count)}</p>

A key nobody has translated renders as itself. The value is a plain string, escaped like any
other. See `MishkaCmsCore.Runtime.Translations.translate/2`.

---

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